//
//  © 2019-2020 Qualcomm Technologies, Inc. and/or its subsidiaries. All rights reserved.
//

#import "IoTPlayer+CategoriesHeader.h"

@interface IoTEqualizerBand ()
@property (nonatomic, assign) double centerFrequency;
@end

@implementation IoTEqualizerBand

+ (IoTEqualizerPropertyAttr *)attrWithBands:(NSArray<IoTEqualizerBand *> *)bands
                               originalAttr:(IoTEqualizerPropertyAttr *)originalAttr
{
    IoTEqualizerPropertyAttr *newAttr = [originalAttr copy];

    double min = originalAttr.levelRange.min;
    double max = originalAttr.levelRange.max;
    NSMutableArray<NSNumber *> *newLevels = [NSMutableArray array];
    for (IoTEqualizerBand *band in bands) {
        double normalizedValue = MAX(MIN(band.bandLevel, 1.0), 0.0);
        double setterValue = min + (normalizedValue * (max - min));
        [newLevels addObject:@(setterValue)];
    }
    newAttr.bandLevel = newLevels;
    newAttr.numBands = (int)newLevels.count;
    return newAttr;
}

+ (NSArray<IoTEqualizerBand *> *)bandsFromAttr:(IoTEqualizerPropertyAttr *)attr
{
    NSMutableArray<IoTEqualizerBand *> *bands = [NSMutableArray array];
    double min = attr.levelRange.min;
    double max = attr.levelRange.max;
    int fewestBands = (int)MIN(MIN(/*attr.numBands, */INT_MAX, attr.bandLevel.count), attr.centerFrequency.count);
    for (int i = 0; i < fewestBands; i++) {
        NSNumber *level = attr.bandLevel[i];
        NSNumber *centerFrequency = attr.centerFrequency[i];

        double value = [level doubleValue];
        double normalizedValue = (value - min) / (max - min);
        normalizedValue = MAX(MIN(normalizedValue, 1.0), 0.0);

        IoTEqualizerBand *band = [[IoTEqualizerBand alloc] init];
        band.centerFrequency = [centerFrequency doubleValue];
        band.bandLevel = normalizedValue;
        [bands addObject:band];
    }
    return [bands copy];
}

@end


@implementation IoTPlayer (AudioEffects)

- (BOOL)bassboostSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.bassboostAttr)
}

- (BOOL)bassboostEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.bassboostAttr, enabled, NO)
}

- (double)bassBoost
{
    RETURN_GETTER_WITH_RANGE(double, audioAdjustmentState.bassboostAttr, strength, strengthRange, 0)
}

- (void)setBassboostEnabled:(BOOL)bassboostEnabled
                 completion:(nullable IoTCompletionHandler)completion
            completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.bassboostAttr,enabled,bassboostEnabled,postBassboostWith)
}

- (void)setBassboost:(double)value // Between 0->1
          completion:(nullable IoTCompletionHandler)completion
     completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER_WITH_RANGE(audioAdjustmentState.bassboostAttr, strength, strengthRange, value, postBassboostWith)
}

- (BOOL)eqSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.equalizerAttr)
}

- (BOOL)eqEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.equalizerAttr, enabled, NO)
}

- (NSString *)currentEQPreset
{
    RETURN_GETTER(NSString *, audioAdjustmentState.equalizerAttr, currentPreset, @"")
}

- (NSArray<NSString *> *)availableEQPresets
{
    RETURN_GETTER(NSArray<NSString *> *, audioAdjustmentState.equalizerAttr, presets, @[])
}

- (NSArray<IoTEqualizerBand *> *)eqBands
{
    if (![self eqEnabled]) {
        return @[];
    }

    IoTEqualizerPropertyAttr *bandsAttr = self.state.audioAdjustmentState.equalizerAttr.equalizerProperty;
    return [IoTEqualizerBand bandsFromAttr:bandsAttr];
}

- (void)setEQEnabled:(BOOL)eqEnabled
          completion:(nullable IoTCompletionHandler)completion
     completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.equalizerAttr,enabled,eqEnabled,postEqualizerWith)
}

- (void)setCurrentEQPreset:(NSString *)eqPreset
                completion:(nullable IoTCompletionHandler)completion
           completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.equalizerAttr,currentPreset,eqPreset,postEqualizerWith)
}

- (void)setEQBands:(NSArray<IoTEqualizerBand *> *)eqBands
        completion:(nullable IoTCompletionHandler)completion
   completionQueue:(nullable dispatch_queue_t)queue
{

    IoTEqualizerPropertyAttr *oldAttr = self.state.audioAdjustmentState.equalizerAttr.equalizerProperty;
    IoTEqualizerPropertyAttr *newAttr = [IoTEqualizerBand attrWithBands:eqBands
                                                           originalAttr:oldAttr];
    SETTER(audioAdjustmentState.equalizerAttr,equalizerProperty,newAttr,postEqualizerWith)
}

- (BOOL)reverbSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.reverbAttr)
}

- (BOOL)reverbEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.reverbAttr, enabled, NO)
}

- (NSString *)currentReverbPreset
{
    RETURN_GETTER(NSString *, audioAdjustmentState.reverbAttr, currentPreset, @"")
}

- (NSArray<NSString *> *)availableReverbPresets
{
    RETURN_GETTER(NSArray<NSString *> *, audioAdjustmentState.reverbAttr, presets, @[])
}

- (void)setReverbEnabled:(BOOL)reverbEnabled
              completion:(nullable IoTCompletionHandler)completion
         completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.reverbAttr,enabled,reverbEnabled,postPresetReverbWith)
}

- (void)setCurrentReverbPreset:(NSString *)reverbPreset
                    completion:(nullable IoTCompletionHandler)completion
               completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.reverbAttr,currentPreset,reverbPreset,postPresetReverbWith)
}

- (BOOL)dolbySupported
{
    RETURN_SUPPORTED(audioAdjustmentState.dolbyAttr)
}

- (BOOL)dolbyEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.dolbyAttr, enabled, NO)
}

- (int)currentDolbyMode
{
    RETURN_GETTER(int, audioAdjustmentState.dolbyAttr, mode, 0)
}

- (int)maxDolbyMode
{
    return 2;
}

- (void)setDolbyEnabled:(BOOL)dolbyEnabled
             completion:(nullable IoTCompletionHandler)completion
        completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.dolbyAttr,enabled,dolbyEnabled,postDolbyWith)
}

- (void)setCurrentDolbyMode:(int)dolbyMode
                 completion:(nullable IoTCompletionHandler)completion
            completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.dolbyAttr,mode,dolbyMode,postDolbyWith)
}

- (BOOL)virtualXSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.virtualXAttr)
}

- (BOOL)virtualXEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.virtualXAttr, enabled, NO)
}

- (BOOL)isDialogClarityEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.virtualXAttr, dialogClarityEnabled, NO)
}

- (void)setVirtualXEnabled:(BOOL)virtualXEnabled
                completion:(nullable IoTCompletionHandler)completion
           completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.virtualXAttr,enabled,virtualXEnabled,postVirtualXSoundXWith)
}

- (void)setDialogClarityEnabled:(BOOL)enabled
                     completion:(nullable IoTCompletionHandler)completion
                completionQueue:(nullable dispatch_queue_t)queue;
{
    SETTER(audioAdjustmentState.virtualXAttr,dialogClarityEnabled,enabled,postVirtualXSoundXWith)
}


- (int)currentVirtualXOutputMode
{
    RETURN_GETTER(int, audioAdjustmentState.virtualXAttr, outMode, 0)
}

- (int)maxVirtualXOutputMode
{
    RETURN_GETTER(int, audioAdjustmentState.virtualXAttr, outModeRange.max, -1)
}

- (void)setCurrentVirtualXOutputMode:(int)outputMode
                          completion:(nullable IoTCompletionHandler)completion
                     completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.virtualXAttr,outMode,outputMode,postVirtualXSoundXWith)
}

#pragma mark Audio Config - Trumpet

#define TRUMPET_GAIN_MAX 30.0
#define TRUMPET_GAIN_MIN -130.0

#define TRUMPET_EQ_GAIN_MAX 576.0
#define TRUMPET_EQ_GAIN_MIN -576.0

- (BOOL)trumpetSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.trumpetAttr);
}

- (BOOL)trumpetEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.trumpetAttr, enabled, NO)
}

- (BOOL)trumpetVirtualizationEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.trumpetAttr, virtualization, NO)
}

- (IoTPreset)currentTrumpetPreset
{
    RETURN_GETTER(IoTPreset, audioAdjustmentState.trumpetAttr, preset, IoTPresetOff)
}

- (int)maxTrumpetPreset
{
    return 5;
}

- (double)trumpetGain
{
    if (!self.trumpetEnabled) {
        return 0;
    }

    double value = self.state.audioAdjustmentState.trumpetAttr.gain;
    double min = TRUMPET_GAIN_MIN;
    double max = TRUMPET_GAIN_MAX;
    double normalizedValue = (value - min) / (max - min);
    normalizedValue = MAX(MIN(normalizedValue, 1.0), 0.0);

    return normalizedValue;
}

- (NSArray<IoTEqualizerBand *> *)trumpetEqBands
{
    if (![self trumpetEnabled]) {
        return @[];
    }

    NSArray<IoTTrumpetEqualizerAttr *> *eqAttrs = self.state.audioAdjustmentState.trumpetAttr.equalizer;
    NSMutableArray<IoTEqualizerBand *> *bands = [[NSMutableArray alloc] initWithCapacity:eqAttrs.count];
    for (IoTTrumpetEqualizerAttr* eqAttr in eqAttrs) {
        IoTEqualizerBand *band = [[IoTEqualizerBand alloc] init];
        band.centerFrequency = eqAttr.frequency;

        double normalizedValue = (eqAttr.gain - TRUMPET_EQ_GAIN_MIN) / (TRUMPET_EQ_GAIN_MAX - TRUMPET_EQ_GAIN_MIN);
        normalizedValue = MAX(MIN(normalizedValue, 1.0), 0.0);
        band.bandLevel = normalizedValue;
        [bands addObject:band];
    }

    return [bands copy];
}

- (void)setTrumpetEnabled:(BOOL)trumpetEnabled
               completion:(nullable IoTCompletionHandler)completion
          completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.trumpetAttr,enabled,trumpetEnabled,postEffectsTrumpetWith)
}

- (void)setTrumpetVirtualizationEnabled:(BOOL)trumpetVirtualizationEnabled
                             completion:(nullable IoTCompletionHandler)completion
                        completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.trumpetAttr,virtualization,trumpetVirtualizationEnabled,postEffectsTrumpetWith)
}

- (void)setCurrentTrumpetPreset:(IoTPreset)trumpetPreset
                     completion:(nullable IoTCompletionHandler)completion
                completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.trumpetAttr,preset,trumpetPreset,postEffectsTrumpetWith)
}


- (void)setTrumpetEQBands:(NSArray<IoTEqualizerBand *> *)eqBands
               completion:(nullable IoTCompletionHandler)completion
          completionQueue:(nullable dispatch_queue_t)queue
{
    if (eqBands.count != self.state.audioAdjustmentState.trumpetAttr.equalizer.count) {
        if (completion && queue) {
            dispatch_async(queue, ^{
                completion(NO, [NSError errorWithIoTError:IoTErrorRequest description:@"Number of bands doesn't match"]);
            });
        }
        return;
    }

    NSArray<IoTTrumpetEqualizerAttr *> *attrBands = [self.state.audioAdjustmentState.trumpetAttr.equalizer copy];
    for (int i = 0; i < eqBands.count; i++) {
        IoTEqualizerBand *band = eqBands[i];
        double normalizedValue = MAX(MIN(band.bandLevel, 1.0), 0.0);
        double setterValue = TRUMPET_EQ_GAIN_MIN + (normalizedValue * (TRUMPET_EQ_GAIN_MAX - TRUMPET_EQ_GAIN_MIN));
        attrBands[i].gain = setterValue;
    }

    SETTER(audioAdjustmentState.trumpetAttr,equalizer,attrBands,postEffectsTrumpetWith)
}

- (void)setTrumpetGain:(double)gain // Between 0->1
            completion:(nullable IoTCompletionHandler)completion
       completionQueue:(nullable dispatch_queue_t)queue
{
    double min = TRUMPET_GAIN_MIN;
    double max = TRUMPET_GAIN_MAX;

    double normalizedValue = MAX(MIN(gain, 1.0), 0.0);
    double setterValue = min + (normalizedValue * (max - min));
    SETTER(audioAdjustmentState.trumpetAttr,gain,setterValue,postEffectsTrumpetWith)
}

#pragma mark Audio Config - MeloD

- (BOOL)meloDSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr);
}

- (BOOL)meloDAroundProSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.aroundPro);
}

- (BOOL)meloDBassManagementSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.bassManagement);
}

- (BOOL)meloDBtlToneCtrlSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.btlToneCtrl);
}

- (BOOL)meloDCoppBassPlusSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.coppBassPlus);
}

- (BOOL)meloDCoppMultiBandLimiterSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.coppMultiBandLimiter);
}

- (BOOL)meloDCoppPEQSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.coppPeq);
}

- (BOOL)meloDCoppVoicePlusSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.coppVoicePlus);
}

- (BOOL)meloDCoppXOverFilterSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.coppXOverFilter);
}

- (BOOL)meloDDRCSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.drc);
}

- (BOOL)meloDGEQSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.geq);
}

- (BOOL)meloDMatrixSupported
{
    RETURN_SUPPORTED(audioAdjustmentState.meloDAttr.matrix);
}

- (BOOL)meloDAroundProEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, aroundPro.enable, NO)
}

- (void)setMeloDAroundProEnabled:(BOOL)enabled
                      completion:(nullable IoTCompletionHandler)completion
                 completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,aroundPro.enable,enabled,postMeloDWith)
}

- (IoTAroundProEffect)meloDAroundProEffect
{
    RETURN_GETTER(IoTAroundProEffect, audioAdjustmentState.meloDAttr, aroundPro.aroundProEffect, IoTAroundProEffectOff)
}

- (void)setMeloDAroundProEffect:(IoTAroundProEffect)effect
                     completion:(nullable IoTCompletionHandler)completion
                completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,aroundPro.aroundProEffect,effect,postMeloDWith)
}

- (IoTAroundProMode)meloDAroundProMode
{
    RETURN_GETTER(IoTAroundProMode, audioAdjustmentState.meloDAttr, aroundPro.aroundProMode, IoTAroundProModeMovie)
}

- (void)setMeloDAroundProMode:(IoTAroundProMode)mode
                   completion:(nullable IoTCompletionHandler)completion
              completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,aroundPro.aroundProMode,mode,postMeloDWith)
}

#pragma mark -

- (BOOL)meloDBassManagementEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, bassManagement.enable, NO)
}

- (void)setMeloDBassManagementEnabled:(BOOL)enabled
                           completion:(nullable IoTCompletionHandler)completion
                      completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,bassManagement.enable,enabled,postMeloDWith)
}

- (double)meloDBassManagementCornerFreqBass
{
    RETURN_GETTER_WITH_RANGE(double, audioAdjustmentState.meloDAttr, bassManagement.cornerFreqBassSlider, bassManagement.cornerFreqBassRange, 0.5)
}

- (void)setMeloDBassManagementCornerFreqBass:(double)value
                                  completion:(nullable IoTCompletionHandler)completion
                             completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER_WITH_RANGE(audioAdjustmentState.meloDAttr, bassManagement.cornerFreqBassSlider, bassManagement.cornerFreqBassRange, value, postMeloDWith)
}

- (BOOL)meloDBassManagementLeftSurroundHeight
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, bassManagement.spkSurrHeightLeft, NO)
}

- (void)setMeloDBassManagementLeftSurroundHeight:(BOOL)enabled
                                      completion:(nullable IoTCompletionHandler)completion
                                 completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,bassManagement.spkSurrHeightLeft,enabled,postMeloDWith)
}

- (BOOL)meloDBassManagementRightSurroundHeight
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, bassManagement.spkSurrHeightRight, NO)
}

- (void)setMeloDBassManagementRightSurroundHeight:(BOOL)enabled
                                       completion:(nullable IoTCompletionHandler)completion
                                  completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,bassManagement.spkSurrHeightRight,enabled,postMeloDWith)
}

- (BOOL)meloDBassManagementSubwooferOut
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, bassManagement.subWooferOut, NO)
}

- (void)setMeloDBassManagementSubwooferOut:(BOOL)enabled
                                completion:(nullable IoTCompletionHandler)completion
                           completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,bassManagement.subWooferOut,enabled,postMeloDWith)
}

- (IoTSurroundSpeakerLeft)meloDBassManagementLeftSurroundSize
{
    RETURN_GETTER(IoTSurroundSpeakerLeft, audioAdjustmentState.meloDAttr, bassManagement.surroundSpeakerLeft, IoTSurroundSpeakerLeftNo)
}

- (void)setMeloDBassManagementLeftSurroundSize:(IoTSurroundSpeakerLeft)size
                                    completion:(nullable IoTCompletionHandler)completion
                               completionQueue:(nullable dispatch_queue_t)queue
{
	SETTER(audioAdjustmentState.meloDAttr,bassManagement.surroundSpeakerLeft,size,postMeloDWith)
}

- (IoTSurroundSpeakerRight)meloDBassManagementRightSurroundSize
{
    RETURN_GETTER(IoTSurroundSpeakerRight, audioAdjustmentState.meloDAttr, bassManagement.surroundSpeakerRight, IoTSurroundSpeakerRightNo)
}

- (void)setMeloDBassManagementRightSurroundSize:(IoTSurroundSpeakerRight)size
                                     completion:(nullable IoTCompletionHandler)completion
                                completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,bassManagement.surroundSpeakerRight,size,postMeloDWith)
}

#pragma mark -

- (BOOL)meloDBTLToneControlEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, btlToneCtrl.enable, NO)
}

- (void)setMeloDBTLToneControlEnabled:(BOOL)enabled
                           completion:(nullable IoTCompletionHandler)completion
                      completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,btlToneCtrl.enable,enabled,postMeloDWith)
}

- (double)meloDBTLToneControlBass
{
    RETURN_GETTER_WITH_RANGE(double, audioAdjustmentState.meloDAttr, btlToneCtrl.bass, btlToneCtrl.bassRange, 0.5)
}

- (void)setMeloDBTLToneControlBass:(double)value
                        completion:(nullable IoTCompletionHandler)completion
                   completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER_WITH_RANGE(audioAdjustmentState.meloDAttr, btlToneCtrl.bass, btlToneCtrl.bassRange, value, postMeloDWith)
}

- (double)meloDBTLToneControlTreble
{
    RETURN_GETTER_WITH_RANGE(double, audioAdjustmentState.meloDAttr, btlToneCtrl.treble, btlToneCtrl.trebleRange, 0.5)
}

- (void)setMeloDBTLToneControlTreble:(double)value
                          completion:(nullable IoTCompletionHandler)completion
                     completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER_WITH_RANGE(audioAdjustmentState.meloDAttr, btlToneCtrl.treble, btlToneCtrl.trebleRange, value, postMeloDWith)
}

- (double)meloDBTLToneControlLoudness
{
    RETURN_GETTER_WITH_RANGE(double, audioAdjustmentState.meloDAttr, btlToneCtrl.loudness, btlToneCtrl.loudnessRange, 0.5)
}

- (void)setMeloDBTLToneControlLoudness:(double)value
                            completion:(nullable IoTCompletionHandler)completion
                       completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER_WITH_RANGE(audioAdjustmentState.meloDAttr, btlToneCtrl.loudness, btlToneCtrl.loudnessRange, value, postMeloDWith)
}

#pragma mark -

- (BOOL)meloDDRCEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, drc.enable, NO)
}

- (void)setMeloDDRCEnabled:(BOOL)enabled
                completion:(nullable IoTCompletionHandler)completion
           completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,drc.enable,enabled,postMeloDWith)
}

- (double)meloDDRCRefLevel
{
    RETURN_GETTER_WITH_RANGE(double, audioAdjustmentState.meloDAttr, drc.refLevel, drc.refLevelRange, 0.5)
}

- (void)setMeloDDRCRefLevel:(double)value
                 completion:(nullable IoTCompletionHandler)completion
            completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER_WITH_RANGE(audioAdjustmentState.meloDAttr, drc.refLevel, drc.refLevelRange, value, postMeloDWith)
}

- (IoTDrcMode)meloDDRCMode
{
    RETURN_GETTER(IoTDrcMode, audioAdjustmentState.meloDAttr, drc.drcMode, IoTDrcModeOff)
}

- (void)setMeloDDRCMode:(IoTDrcMode)mode
             completion:(nullable IoTCompletionHandler)completion
        completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr, drc.drcMode, mode, postMeloDWith)
}

#pragma mark -

- (BOOL)meloDGEQEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, geq.enable, NO)
}

- (void)setMeloDGEQEnabled:(BOOL)enabled
                completion:(nullable IoTCompletionHandler)completion
           completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,geq.enable,enabled,postMeloDWith)
}

- (NSArray<IoTEqualizerBand *> *)meloDGEQBands
{
    if (![self meloDGEQEnabled]) {
        return @[];
    }

    IoTEqualizerPropertyAttr *bandsAttr = self.state.audioAdjustmentState.meloDAttr.geq.geqProperty;
    return [IoTEqualizerBand bandsFromAttr:bandsAttr];
}

- (void)setMeloDGEQBands:(NSArray<IoTEqualizerBand *> *)eqBands
              completion:(nullable IoTCompletionHandler)completion
         completionQueue:(nullable dispatch_queue_t)queue
{

    IoTEqualizerPropertyAttr *oldAttr = self.state.audioAdjustmentState.meloDAttr.geq.geqProperty;
    IoTEqualizerPropertyAttr *newAttr = [IoTEqualizerBand attrWithBands:eqBands
                                                           originalAttr:oldAttr];
    SETTER(audioAdjustmentState.meloDAttr,geq.geqProperty,newAttr,postMeloDWith)
}

- (NSString *)currentMeloDGEQPreset
{
    RETURN_GETTER(NSString *, audioAdjustmentState.meloDAttr, geq.currentPreset, @"")
}

- (NSArray<NSString *> *)availableMeloDGEQPresets
{
    RETURN_GETTER(NSArray<NSString *> *, audioAdjustmentState.meloDAttr, geq.presets, @[])
}

- (void)setCurrentMeloDGEQPreset:(NSString *)eqPreset
                      completion:(nullable IoTCompletionHandler)completion
                 completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,geq.currentPreset,eqPreset,postMeloDWith)
}

#pragma mark

- (BOOL)meloDMatrixEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, matrix.enable, NO)
}

- (void)setMeloDMatrixEnabled:(BOOL)enabled
                   completion:(nullable IoTCompletionHandler)completion
              completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,matrix.enable,enabled,postMeloDWith)
}

- (IoTMatrixMode)meloDMatrixMode
{
    RETURN_GETTER(IoTMatrixMode, audioAdjustmentState.meloDAttr, matrix.matrixMode, IoTMatrixModeMovie)
}

- (void)setMeloDMatrixMode:(IoTMatrixMode)mode
                completion:(nullable IoTCompletionHandler)completion
           completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr, matrix.matrixMode, mode, postMeloDWith)
}

#pragma mark

- (BOOL)meloDCOPPBassPlusEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, coppBassPlus.enable, NO)
}

- (void)setMeloDCOPPBassPlusEnabled:(BOOL)enabled
                         completion:(nullable IoTCompletionHandler)completion
                    completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,coppBassPlus.enable,enabled,postMeloDWith)
}

- (double)meloDCOPPBassPlusHarmonic
{
    RETURN_GETTER_WITH_RANGE(double, audioAdjustmentState.meloDAttr, coppBassPlus.harmonic, coppBassPlus.harmonicRange, 0.5)
}

- (void)setMeloDCOPPBassPlusHarmonic:(double)value
                          completion:(nullable IoTCompletionHandler)completion
                     completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER_WITH_RANGE(audioAdjustmentState.meloDAttr, coppBassPlus.harmonic, coppBassPlus.harmonicRange, value, postMeloDWith)
}

- (double)meloDCOPPBassPlusStrength
{
    RETURN_GETTER_WITH_RANGE(double, audioAdjustmentState.meloDAttr, coppBassPlus.strength, coppBassPlus.strengthRange, 0.5)
}

- (void)setMeloDCOPPBassPlusStrength:(double)value
                          completion:(nullable IoTCompletionHandler)completion
                     completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER_WITH_RANGE(audioAdjustmentState.meloDAttr, coppBassPlus.strength, coppBassPlus.strengthRange, value, postMeloDWith)
}

#pragma mark -

- (BOOL)meloDCOPPVoicePlusEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, coppVoicePlus.enable, NO)
}

- (void)setMeloDCOPPVoicePlusEnabled:(BOOL)enabled
                          completion:(nullable IoTCompletionHandler)completion
                     completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,coppVoicePlus.enable,enabled,postMeloDWith)
}

- (IoTVoicePlusEffect)meloDVoicePlusEffect
{
    RETURN_GETTER(IoTVoicePlusEffect, audioAdjustmentState.meloDAttr, coppVoicePlus.voicePlusEffect, IoTVoicePlusEffectOff)
}

- (void)setMeloDVoicePlusEffect:(IoTVoicePlusEffect)effect
                completion:(nullable IoTCompletionHandler)completion
           completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr, coppVoicePlus.voicePlusEffect, effect, postMeloDWith)
}

#pragma -

- (BOOL)meloDCOPPMultibandLimiterEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, coppMultiBandLimiter.enable, NO)
}

- (void)setMeloDCOPPMultibandLimiterEnabled:(BOOL)enabled
                                 completion:(nullable IoTCompletionHandler)completion
                            completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,coppMultiBandLimiter.enable,enabled,postMeloDWith)
}

- (BOOL)meloDCOPPPEQEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, coppPeq.enable, NO)
}

- (void)setMeloDCOPPPEQEnabled:(BOOL)enabled
                    completion:(nullable IoTCompletionHandler)completion
               completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,coppPeq.enable,enabled,postMeloDWith)
}

- (BOOL)meloDCOPPXOverFilterEnabled
{
    RETURN_GETTER(BOOL, audioAdjustmentState.meloDAttr, coppXOverFilter.enable, NO)
}

- (void)setMeloDCOPPXOverFilterEnabled:(BOOL)enabled
                            completion:(nullable IoTCompletionHandler)completion
                       completionQueue:(nullable dispatch_queue_t)queue
{
    SETTER(audioAdjustmentState.meloDAttr,coppXOverFilter.enable,enabled,postMeloDWith)
}

@end
