From 6f47656dfe7a1ef77c01abba09d401420a443bde Mon Sep 17 00:00:00 2001
From: Samuel Bassaly <shkshk.90@gmail.com>
Date: Mon, 23 Aug 2021 01:42:25 +0200
Subject: [PATCH] macosx: Add stereo_pan filter

Add stereo pan filter to Advanced Audio Effects panel.

Add enable button for each filter, as pan audio effect has effect over
the whole spectrum (always noticeable), thus, if always enabled, it
makes pitch adjustment unusable.

Additionally, for pitch adjustment:
- Update the step of the scale to 0.25
- Fix the default value to 0.0 (was previously 0.25)

Closes issue #25872
---
 modules/gui/macosx/UI/AudioEffects.xib        |  92 ++++++++++------
 .../panels/VLCAudioEffectsWindowController.h  |  12 +-
 .../panels/VLCAudioEffectsWindowController.m  | 103 ++++++++++++------
 3 files changed, 135 insertions(+), 72 deletions(-)

diff --git a/modules/gui/macosx/UI/AudioEffects.xib b/modules/gui/macosx/UI/AudioEffects.xib
index 37c7b458311d..81d151b03b4f 100644
--- a/modules/gui/macosx/UI/AudioEffects.xib
+++ b/modules/gui/macosx/UI/AudioEffects.xib
@@ -8,11 +8,13 @@
     <objects>
         <customObject id="-2" userLabel="File's Owner" customClass="VLCAudioEffectsWindowController">
             <connections>
-                <outlet property="advancedEnableCheckbox" destination="rjL-0q-isR" id="Tjh-on-9sb"/>
-                <outlet property="advancedPitchLabel" destination="f9i-Ql-mCB" id="9nt-SB-ROX"/>
+                <outlet property="advancedEnablePitchCheckBox" destination="rjL-0q-isR" id="uT4-QE-Mdc"/>
+                <outlet property="advancedEnableStereoPanCheckBox" destination="T4W-oT-4At" id="aJK-Hf-oWv"/>
                 <outlet property="advancedPitchSlider" destination="nTJ-ss-QlS" id="mPt-X9-kEF"/>
                 <outlet property="advancedPitchTextField" destination="8cX-Go-0bh" id="5pd-hu-267"/>
                 <outlet property="advancedResetButton" destination="fI0-dm-MVt" id="Yg0-gT-4je"/>
+                <outlet property="advancedStereoPanSlider" destination="sxu-tL-Ztr" id="wix-Eu-RX7"/>
+                <outlet property="advancedStereoPanTextField" destination="Fbj-Sr-i4z" id="vFF-M0-QUy"/>
                 <outlet property="advancedView" destination="L1S-G0-kAE" id="lDO-b5-pKe"/>
                 <outlet property="applyProfileCheckbox" destination="Dqv-cc-ZvK" id="DXd-0m-de4"/>
                 <outlet property="compressorBand1Label" destination="248" id="qCX-jv-cwZ"/>
@@ -101,7 +103,7 @@
             <windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" HUD="YES"/>
             <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
             <rect key="contentRect" x="196" y="254" width="473" height="281"/>
-            <rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
+            <rect key="screenRect" x="0.0" y="0.0" width="3440" height="1440"/>
             <view key="contentView" id="2">
                 <rect key="frame" x="0.0" y="0.0" width="475" height="281"/>
                 <autoresizingMask key="autoresizingMask"/>
@@ -1136,18 +1138,8 @@
                                     <rect key="frame" x="0.0" y="0.0" width="475" height="211"/>
                                     <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                     <subviews>
-                                        <button mirrorLayoutDirectionWhenInternationalizing="always" translatesAutoresizingMaskIntoConstraints="NO" id="rjL-0q-isR">
-                                            <rect key="frame" x="7" y="190" width="58" height="18"/>
-                                            <buttonCell key="cell" type="check" title="Enable" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="XkH-KA-7ZB">
-                                                <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
-                                                <font key="font" metaFont="message" size="11"/>
-                                            </buttonCell>
-                                            <connections>
-                                                <action selector="advancedEnable:" target="-2" id="0wk-x7-VxI"/>
-                                            </connections>
-                                        </button>
                                         <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fI0-dm-MVt">
-                                            <rect key="frame" x="426" y="191" width="39" height="15"/>
+                                            <rect key="frame" x="426" y="190" width="39" height="15"/>
                                             <buttonCell key="cell" type="roundRect" title="Reset" bezelStyle="roundedRect" image="buttonCell:rEJ-M5-S37:image" imagePosition="overlaps" alignment="center" controlSize="mini" borderStyle="border" inset="2" id="rEJ-M5-S37">
                                                 <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
                                                 <font key="font" metaFont="miniSystem"/>
@@ -1157,57 +1149,85 @@
                                             </connections>
                                         </button>
                                         <customView translatesAutoresizingMaskIntoConstraints="NO" id="L1S-G0-kAE">
-                                            <rect key="frame" x="195" y="0.0" width="86" height="178"/>
+                                            <rect key="frame" x="151" y="0.0" width="174" height="177"/>
                                             <subviews>
-                                                <slider translatesAutoresizingMaskIntoConstraints="NO" id="nTJ-ss-QlS">
-                                                    <rect key="frame" x="34" y="47" width="18" height="131"/>
-                                                    <sliderCell key="cell" controlSize="small" continuous="YES" alignment="left" minValue="-12" maxValue="12" tickMarkPosition="right" numberOfTickMarks="9" sliderType="linear" id="kju-14-quf"/>
+                                                <button mirrorLayoutDirectionWhenInternationalizing="always" translatesAutoresizingMaskIntoConstraints="NO" id="rjL-0q-isR" userLabel="Enable Pitch">
+                                                    <rect key="frame" x="2" y="10" width="80" height="18"/>
+                                                    <buttonCell key="cell" type="check" title="Adjust Pitch" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="XkH-KA-7ZB">
+                                                        <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+                                                        <font key="font" metaFont="system" size="10"/>
+                                                    </buttonCell>
                                                     <connections>
-                                                        <action selector="advancedSliderUpdated:" target="-2" id="yzS-3w-HKN"/>
+                                                        <action selector="advancedEnablePitchAction:" target="-2" id="HjV-I0-cAu"/>
+                                                    </connections>
+                                                </button>
+                                                <button mirrorLayoutDirectionWhenInternationalizing="always" translatesAutoresizingMaskIntoConstraints="NO" id="T4W-oT-4At" userLabel="Enable Pan">
+                                                    <rect key="frame" x="95" y="10" width="74" height="18"/>
+                                                    <buttonCell key="cell" type="check" title="Adjust Pan" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" inset="2" id="spd-gN-jF7">
+                                                        <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+                                                        <font key="font" metaFont="system" size="10"/>
+                                                    </buttonCell>
+                                                    <connections>
+                                                        <action selector="advancedEnableStereoPanAction:" target="-2" id="pl8-yv-9vM"/>
+                                                    </connections>
+                                                </button>
+                                                <slider translatesAutoresizingMaskIntoConstraints="NO" id="nTJ-ss-QlS" userLabel="Pitch Slider">
+                                                    <rect key="frame" x="34" y="48" width="18" height="129"/>
+                                                    <sliderCell key="cell" controlSize="small" continuous="YES" alignment="left" minValue="-48" maxValue="48" tickMarkPosition="right" numberOfTickMarks="9" sliderType="linear" id="kju-14-quf"/>
+                                                    <connections>
+                                                        <action selector="advancedPitchSliderUpdated:" target="-2" id="Zz3-Jj-zs3"/>
                                                     </connections>
                                                 </slider>
                                                 <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="755" translatesAutoresizingMaskIntoConstraints="NO" id="8cX-Go-0bh">
-                                                    <rect key="frame" x="8" y="33" width="70" height="11"/>
+                                                    <rect key="frame" x="8" y="34" width="70" height="11"/>
                                                     <textFieldCell key="cell" controlSize="mini" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="0.0 semitones" usesSingleLineMode="YES" id="Ycq-EF-cb9">
                                                         <font key="font" metaFont="miniSystem"/>
                                                         <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                         <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                                     </textFieldCell>
                                                 </textField>
-                                                <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="f9i-Ql-mCB" customClass="VLCWrappableTextField">
-                                                    <rect key="frame" x="8" y="13" width="70" height="11"/>
-                                                    <textFieldCell key="cell" controlSize="mini" sendsActionOnEndEditing="YES" alignment="center" title="Adjust pitch" id="tFs-LA-Bdx">
+                                                <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="755" translatesAutoresizingMaskIntoConstraints="NO" id="Fbj-Sr-i4z" userLabel="StreoPannerScale">
+                                                    <rect key="frame" x="122" y="34" width="22" height="11"/>
+                                                    <textFieldCell key="cell" controlSize="mini" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="0.0" usesSingleLineMode="YES" id="Inp-23-uMg">
                                                         <font key="font" metaFont="miniSystem"/>
                                                         <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                         <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                                     </textFieldCell>
                                                 </textField>
+                                                <slider translatesAutoresizingMaskIntoConstraints="NO" id="sxu-tL-Ztr" userLabel="StereoPanner Slider">
+                                                    <rect key="frame" x="124" y="48" width="18" height="129"/>
+                                                    <sliderCell key="cell" controlSize="small" continuous="YES" alignment="left" maxValue="10" doubleValue="5" tickMarkPosition="right" numberOfTickMarks="9" sliderType="linear" id="XZV-sP-C5o"/>
+                                                    <connections>
+                                                        <action selector="advancedStereoPanSliderUpdated:" target="-2" id="Xeo-cH-SH6"/>
+                                                    </connections>
+                                                </slider>
                                             </subviews>
                                             <constraints>
+                                                <constraint firstItem="Fbj-Sr-i4z" firstAttribute="centerY" secondItem="8cX-Go-0bh" secondAttribute="centerY" id="0pJ-WY-iAF"/>
                                                 <constraint firstItem="nTJ-ss-QlS" firstAttribute="top" secondItem="L1S-G0-kAE" secondAttribute="top" id="6RB-JZ-xWV"/>
-                                                <constraint firstAttribute="trailing" secondItem="8cX-Go-0bh" secondAttribute="trailing" constant="10" id="Jg8-TQ-Djp"/>
-                                                <constraint firstAttribute="bottom" secondItem="f9i-Ql-mCB" secondAttribute="bottom" constant="13" id="brb-yM-ZgX"/>
-                                                <constraint firstItem="nTJ-ss-QlS" firstAttribute="bottom" secondItem="f9i-Ql-mCB" secondAttribute="top" constant="-24" id="eZX-RP-sUs"/>
-                                                <constraint firstItem="8cX-Go-0bh" firstAttribute="leading" secondItem="L1S-G0-kAE" secondAttribute="leading" constant="10" id="hRo-i2-MvS"/>
+                                                <constraint firstItem="rjL-0q-isR" firstAttribute="top" secondItem="8cX-Go-0bh" secondAttribute="bottom" constant="9" id="6Zv-RC-wWK"/>
+                                                <constraint firstItem="T4W-oT-4At" firstAttribute="centerX" secondItem="sxu-tL-Ztr" secondAttribute="centerX" id="728-kt-ufE"/>
+                                                <constraint firstItem="sxu-tL-Ztr" firstAttribute="leading" secondItem="nTJ-ss-QlS" secondAttribute="trailing" constant="72" id="7Ki-5Z-S8b"/>
+                                                <constraint firstItem="Fbj-Sr-i4z" firstAttribute="centerX" secondItem="sxu-tL-Ztr" secondAttribute="centerX" id="B2m-C8-ta1"/>
+                                                <constraint firstItem="8cX-Go-0bh" firstAttribute="centerX" secondItem="nTJ-ss-QlS" secondAttribute="centerX" id="LfM-jX-y1I"/>
+                                                <constraint firstAttribute="bottom" secondItem="rjL-0q-isR" secondAttribute="bottom" constant="13" id="Lrv-ou-kcN"/>
+                                                <constraint firstItem="sxu-tL-Ztr" firstAttribute="centerY" secondItem="nTJ-ss-QlS" secondAttribute="centerY" id="PLl-ZS-7aw"/>
+                                                <constraint firstItem="T4W-oT-4At" firstAttribute="centerY" secondItem="rjL-0q-isR" secondAttribute="centerY" id="RjE-QS-KsO"/>
                                                 <constraint firstItem="nTJ-ss-QlS" firstAttribute="leading" secondItem="L1S-G0-kAE" secondAttribute="leading" constant="34" id="hw4-tY-RXC"/>
                                                 <constraint firstItem="8cX-Go-0bh" firstAttribute="top" secondItem="nTJ-ss-QlS" secondAttribute="bottom" constant="4" id="oit-FY-gc0"/>
-                                                <constraint firstAttribute="trailing" secondItem="f9i-Ql-mCB" secondAttribute="trailing" constant="10" id="rGx-6J-gH4"/>
-                                                <constraint firstItem="f9i-Ql-mCB" firstAttribute="top" secondItem="8cX-Go-0bh" secondAttribute="bottom" constant="9" id="rOG-sZ-pgI"/>
-                                                <constraint firstAttribute="trailing" secondItem="nTJ-ss-QlS" secondAttribute="trailing" constant="34" id="ruG-mu-TTd"/>
-                                                <constraint firstItem="f9i-Ql-mCB" firstAttribute="leading" secondItem="L1S-G0-kAE" secondAttribute="leading" constant="10" id="thK-bH-a3q"/>
+                                                <constraint firstItem="rjL-0q-isR" firstAttribute="centerX" secondItem="nTJ-ss-QlS" secondAttribute="centerX" id="uBH-KL-Id5"/>
+                                                <constraint firstAttribute="trailing" secondItem="sxu-tL-Ztr" secondAttribute="trailing" constant="32" id="uPa-lj-qaE"/>
+                                                <constraint firstItem="sxu-tL-Ztr" firstAttribute="top" secondItem="nTJ-ss-QlS" secondAttribute="top" id="unt-eX-rJ4"/>
                                             </constraints>
                                         </customView>
                                     </subviews>
                                     <constraints>
                                         <constraint firstItem="L1S-G0-kAE" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="209-XL-dUD" secondAttribute="leading" constant="10" id="3r6-Nx-9LO"/>
-                                        <constraint firstItem="L1S-G0-kAE" firstAttribute="top" secondItem="rjL-0q-isR" secondAttribute="bottom" constant="15" id="LbX-hj-NJy"/>
-                                        <constraint firstItem="rjL-0q-isR" firstAttribute="leading" secondItem="209-XL-dUD" secondAttribute="leading" constant="10" id="MNF-lI-670"/>
+                                        <constraint firstItem="L1S-G0-kAE" firstAttribute="top" secondItem="fI0-dm-MVt" secondAttribute="bottom" constant="14" id="LbX-hj-NJy"/>
                                         <constraint firstAttribute="trailing" secondItem="fI0-dm-MVt" secondAttribute="trailing" constant="10" id="OJI-Rf-3G1"/>
+                                        <constraint firstItem="fI0-dm-MVt" firstAttribute="top" secondItem="209-XL-dUD" secondAttribute="top" constant="6" id="T4t-VG-iF4"/>
                                         <constraint firstAttribute="bottom" secondItem="L1S-G0-kAE" secondAttribute="bottom" id="cKC-Jc-Ljo"/>
                                         <constraint firstItem="L1S-G0-kAE" firstAttribute="centerX" secondItem="209-XL-dUD" secondAttribute="centerX" id="coc-Mz-9PD"/>
-                                        <constraint firstItem="rjL-0q-isR" firstAttribute="top" secondItem="209-XL-dUD" secondAttribute="top" constant="6" id="ecd-Pr-3ZQ"/>
-                                        <constraint firstItem="fI0-dm-MVt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="rjL-0q-isR" secondAttribute="trailing" constant="10" id="ita-Ge-bQL"/>
-                                        <constraint firstItem="rjL-0q-isR" firstAttribute="centerY" secondItem="fI0-dm-MVt" secondAttribute="centerY" id="lPZ-lq-8OT"/>
                                         <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="L1S-G0-kAE" secondAttribute="trailing" constant="10" id="qFf-QN-rga"/>
                                     </constraints>
                                 </view>
diff --git a/modules/gui/macosx/panels/VLCAudioEffectsWindowController.h b/modules/gui/macosx/panels/VLCAudioEffectsWindowController.h
index beb954f45e97..24a8721640a8 100644
--- a/modules/gui/macosx/panels/VLCAudioEffectsWindowController.h
+++ b/modules/gui/macosx/panels/VLCAudioEffectsWindowController.h
@@ -118,11 +118,13 @@
 
 /* Advanced */
 @property (readwrite, weak) IBOutlet NSView *advancedView;
-@property (readwrite, weak) IBOutlet NSButton *advancedEnableCheckbox;
+@property (readwrite, weak) IBOutlet NSButton *advancedEnablePitchCheckBox;
+@property (readwrite, weak) IBOutlet NSButton *advancedEnableStereoPanCheckBox;
 @property (readwrite, weak) IBOutlet NSButton *advancedResetButton;
 @property (readwrite, weak) IBOutlet NSSlider *advancedPitchSlider;
 @property (readwrite, weak) IBOutlet NSTextField *advancedPitchTextField;
-@property (readwrite, weak) IBOutlet NSTextField *advancedPitchLabel;
+@property (readwrite, weak) IBOutlet NSSlider *advancedStereoPanSlider;
+@property (readwrite, weak) IBOutlet NSTextField *advancedStereoPanTextField;
 
 /* generic */
 - (IBAction)profileSelectorAction:(id)sender;
@@ -157,7 +159,9 @@
 
 /* Advanced */
 - (IBAction)resetAdvancedValues:(id)sender;
-- (IBAction)advancedEnable:(id)sender;
-- (IBAction)advancedSliderUpdated:(id)sender;
+- (IBAction)advancedPitchSliderUpdated:(id)sender;
+- (IBAction)advancedStereoPanSliderUpdated:(id)sender;
+- (IBAction)advancedEnablePitchAction:(id)sender;
+- (IBAction)advancedEnableStereoPanAction:(id)sender;
 
 @end
diff --git a/modules/gui/macosx/panels/VLCAudioEffectsWindowController.m b/modules/gui/macosx/panels/VLCAudioEffectsWindowController.m
index 5cb57b2ca903..db6f1c6ca726 100644
--- a/modules/gui/macosx/panels/VLCAudioEffectsWindowController.m
+++ b/modules/gui/macosx/panels/VLCAudioEffectsWindowController.m
@@ -50,6 +50,12 @@ NSString *VLCAudioEffectsEqualizerProfileNamesKey = @"EQNames";
 NSString *VLCAudioEffectsProfilesKey = @"AudioEffectProfiles";
 NSString *VLCAudioEffectsProfileNamesKey = @"AudioEffectProfileNames";
 
+static inline void enableTextField(NSTextField *const __unsafe_unretained textField,
+                                   const BOOL enable)
+{
+    [textField setTextColor:enable ? [NSColor controlTextColor] : [NSColor disabledControlTextColor]];
+}
+
 @interface VLCAudioEffectsWindowController ()
 {
     VLCPlayerController *_playerController;
@@ -104,8 +110,8 @@ NSString *VLCAudioEffectsProfileNamesKey = @"AudioEffectProfileNames";
 
 + (NSString *)defaultProfileString
 {
-    return [NSString stringWithFormat:@"ZmxhdA==;;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i;%f",
-            .0,25.,100.,-11.,8.,2.5,7.,.85,1.,.4,.5,.5,2.,0,0.25f];
+    return [NSString stringWithFormat:@"ZmxhdA==;;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i;%f;%f",
+            .0,25.,100.,-11.,8.,2.5,7.,.85,1.,.4,.5,.5,2.,0,0.0,0.5];
 }
 
 - (id)init
@@ -161,6 +167,7 @@ NSString *VLCAudioEffectsProfileNamesKey = @"AudioEffectProfileNames";
     aout_EnableFilter(p_aout, "normvol", false);
     aout_EnableFilter(p_aout, "karaoke", false);
     aout_EnableFilter(p_aout, "scaletempo_pitch", false);
+    aout_EnableFilter(p_aout, "stereo_pan", false);
   
     /* fetch preset */
     NSString *profileString;
@@ -197,16 +204,13 @@ NSString *VLCAudioEffectsProfileNamesKey = @"AudioEffectProfileNames";
     var_SetFloat(p_aout, "norm-max-level", [[items objectAtIndex:14] floatValue]);
     var_SetBool(p_aout, "equalizer-2pass", (BOOL)[[items objectAtIndex:15] intValue]);
     
-    // The old version of the defaults data contains 16 members (without pitch).
+    // The old version of the defaults data contains 16 members (without pitch and pan).
     // These values are used above.
     // In case the user updates VLC, this check handles the 1 time case that the data
     // is still in the old format, and sets the pitch value to the default, otherwise
     // it loads normally the saved pitch value.
-    if (likely(items.count >= 17)) {
-        var_SetFloat(p_aout, "pitch-shift", [[items objectAtIndex:16] floatValue]);
-    } else {
-        var_SetFloat(p_aout, "pitch-shift", 0.25f);
-    }
+    var_SetFloat(p_aout, "pitch-shift", items.count >= 17 ? [[items objectAtIndex:16] floatValue] : 0.0f);
+    var_SetFloat(p_aout, "pan-control", items.count >= 18 ? [[items objectAtIndex:17] floatValue] : 0.5f);
     
     var_SetString(p_aout, "equalizer-bands", [[[defaults objectForKey:VLCAudioEffectsEqualizerValuesKey] objectAtIndex:presetIndex] UTF8String]);
     var_SetFloat(p_aout, "equalizer-preamp", [[[defaults objectForKey:VLCAudioEffectsEqualizerPreampValuesKey] objectAtIndex:presetIndex] floatValue]);
@@ -288,9 +292,9 @@ NSString *VLCAudioEffectsProfileNamesKey = @"AudioEffectProfileNames";
                                                   "thereby widening the stereo effect.")];
     
     /* Advanced */
-    [_advancedEnableCheckbox setTitle:_NS("Enable")];
     [_advancedResetButton setTitle:_NS("Reset")];
-    [_advancedPitchLabel setStringValue:_NS("Adjust pitch")];
+    [_advancedEnablePitchCheckBox setStringValue:_NS("Adjust pitch")];
+    [_advancedEnableStereoPanCheckBox setStringValue:_NS("Adjust pan")];
     
     
     /* generic */
@@ -393,7 +397,7 @@ NSString *VLCAudioEffectsProfileNamesKey = @"AudioEffectProfileNames";
     if (!p_aout)
         return nil;
 
-    return [NSString stringWithFormat:@"%@;%@;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i;%f",
+    return [NSString stringWithFormat:@"%@;%@;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i;%f;%f",
                      B64EncAndFree(var_GetNonEmptyString(p_aout, "equalizer-preset")),
                      B64EncAndFree(var_InheritString(p_aout, "audio-filter")),
                      var_InheritFloat(p_aout, "compressor-rms-peak"),
@@ -410,7 +414,8 @@ NSString *VLCAudioEffectsProfileNamesKey = @"AudioEffectProfileNames";
                      var_InheritFloat(p_aout, "spatializer-damp"),
                      var_InheritFloat(p_aout, "norm-max-level"),
                      var_InheritBool(p_aout, "equalizer-2pass"),
-                     var_InheritFloat(p_aout, "pitch-shift")];
+                     var_InheritFloat(p_aout, "pitch-shift"),
+                     var_InheritFloat(p_aout, "pan-control")];
 }
 
 - (void)saveCurrentProfile
@@ -1220,8 +1225,9 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 #pragma mark Advanced
 - (void)resetAdvanced
 {
-    BOOL bEnable_advanced = NO;
-    char *psz_afilters    = NULL;
+    BOOL bEnable_pitch = NO;
+    BOOL bEnable_pan = NO;
+    char *psz_afilters = NULL;
     
     audio_output_t *p_aout = [_playerController mainAudioOutput];
     if (!p_aout)
@@ -1229,15 +1235,28 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 
     psz_afilters = var_InheritString(p_aout, "audio-filter");
     if (psz_afilters) {
-        bEnable_advanced = strstr(psz_afilters, "scaletempo_pitch") != NULL;
+        bEnable_pitch = strstr(psz_afilters, "scaletempo_pitch") != NULL;
+        bEnable_pan = strstr(psz_afilters, "stereo_pan") != NULL;
+        
         free(psz_afilters);
     }
+    
+    [_advancedEnablePitchCheckBox setState:(bEnable_pitch ? NSControlStateValueOn : NSControlStateValueOff)];
+    [_advancedPitchSlider setEnabled:bEnable_pitch];
+    enableTextField(_advancedPitchTextField, bEnable_pitch);
+    
+    const float pitchValue = var_CreateGetFloat(p_aout, "pitch-shift");
+    [_advancedPitchSlider setFloatValue:pitchValue * 4.0f];
+    [_advancedPitchTextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f semitones", pitchValue]];
+    
 
-    [_advancedView enableSubviews:bEnable_advanced];
-    [_advancedEnableCheckbox setState:(bEnable_advanced ? NSOnState : NSOffState)];
+    [_advancedEnableStereoPanCheckBox setState:(bEnable_pan ? NSControlStateValueOn : NSControlStateValueOff)];
+    [_advancedStereoPanSlider setEnabled:bEnable_pan];
+    enableTextField(_advancedStereoPanTextField, bEnable_pan);
     
-    [_advancedPitchSlider setFloatValue: var_CreateGetFloat(p_aout, "pitch-shift")];
-    [_advancedPitchTextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f semitones", [_advancedPitchSlider floatValue]]];
+    const float panValue = var_CreateGetFloat(p_aout, "pan-control");
+    [_advancedStereoPanSlider setFloatValue: panValue * 10.0f];
+    [_advancedStereoPanTextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", panValue]];
 
     aout_Release(p_aout);
 }
@@ -1246,38 +1265,58 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 {
     audio_output_t *p_aout = [_playerController mainAudioOutput];
     if (p_aout) {
-        var_SetFloat(p_aout, "pitch-shift", 0.25f);
+        var_SetFloat(p_aout, "pitch-shift", 0.0f);
+        var_SetFloat(p_aout, "pan-control", 0.5f);
         aout_Release(p_aout);
     }
     
     [self resetAdvanced];
 }
 
-- (IBAction)advancedSliderUpdated:(id)sender
+- (IBAction)advancedPitchSliderUpdated:(id)sender
 {
-    char *psz_property = nil;
-    float f_value = [sender floatValue];
+    char *const psz_property = "pitch-shift";
+    const float f_value = [sender floatValue] * 0.25; // Scale
 
-    if (sender == _advancedPitchSlider)
-        psz_property = "pitch-shift";
+    audio_output_t *p_aout = [_playerController mainAudioOutput];
+    if (p_aout) {
+        var_SetFloat(p_aout, psz_property, f_value);
+        aout_Release(p_aout);
+    }
 
-    assert(psz_property);
+    [_advancedPitchTextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f semitones", f_value]];
+}
 
+- (IBAction)advancedStereoPanSliderUpdated:(id)sender
+{
+    char *const psz_property = "pan-control";
+    const float f_value = [sender floatValue] * 0.1; // Scale
+    
     audio_output_t *p_aout = [_playerController mainAudioOutput];
     if (p_aout) {
         var_SetFloat(p_aout, psz_property, f_value);
         aout_Release(p_aout);
     }
-
-    if (sender == _advancedPitchSlider)
-        [_advancedPitchTextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f semitones", f_value]];
+    
+    [_advancedStereoPanTextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
 }
 
-- (IBAction)advancedEnable:(id)sender
+- (IBAction)advancedEnablePitchAction:(id)sender
 {
-    [_advancedView enableSubviews:[sender state]];
-    [_playerController enableAudioFilterWithName:@"scaletempo_pitch" state:[sender state]];
+    const BOOL newState = _advancedEnablePitchCheckBox.state == NSControlStateValueOn;
+    
+    enableTextField(_advancedPitchTextField, newState);
+    [_advancedPitchSlider setEnabled:newState];
+    [_playerController enableAudioFilterWithName:@"scaletempo_pitch" state:newState];
 }
 
+- (IBAction)advancedEnableStereoPanAction:(id)sender
+{
+    const BOOL newState = _advancedEnableStereoPanCheckBox.state == NSControlStateValueOn;
+    
+    enableTextField(_advancedStereoPanTextField, newState);
+    [_advancedStereoPanSlider setEnabled:newState];
+    [_playerController enableAudioFilterWithName:@"stereo_pan" state:newState];
+}
 
 @end
-- 
GitLab