macosx: don't reload default audio profile if already loaded
[vlc.git] / modules / gui / macosx / VLCAudioEffectsWindowController.m
blob4b51c697910de8ca88c76b0b8ed5e7b62f4ffc6b
1 /*****************************************************************************
2  * VLCAudioEffectsWindowController.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2004-2017 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8  *          Jérôme Decoodt <djc@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # import "config.h"
27 #endif
29 #import "VLCMain.h"
30 #import "VLCAudioEffectsWindowController.h"
31 #import "../../audio_filter/equalizer_presets.h"
32 #import "CompatibilityFixes.h"
33 #import "VLCPopupPanelController.h"
34 #import "VLCTextfieldPanelController.h"
36 #import <vlc_common.h>
38 #import <math.h>
40 @interface VLCAudioEffectsWindowController ()
42     NSInteger i_old_profile_index;
44 - (void)resetProfileSelector;
45 - (void)updatePresetSelector;
46 - (void)setBandSliderValuesForPreset:(NSInteger)presetID;
47 @end
49 #pragma mark -
50 #pragma mark Initialization
52 @implementation VLCAudioEffectsWindowController
54 + (void)initialize
56     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
58     NSString *workString;
59     NSMutableArray *workValues = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
60     NSMutableArray *workPreamp = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
61     NSMutableArray *workTitles = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
62     NSMutableArray *workNames = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
64     for (int i = 0 ; i < NB_PRESETS ; i++) {
65         workString = [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
66                       eqz_preset_10b[i].f_amp[0],
67                       eqz_preset_10b[i].f_amp[1],
68                       eqz_preset_10b[i].f_amp[2],
69                       eqz_preset_10b[i].f_amp[3],
70                       eqz_preset_10b[i].f_amp[4],
71                       eqz_preset_10b[i].f_amp[5],
72                       eqz_preset_10b[i].f_amp[6],
73                       eqz_preset_10b[i].f_amp[7],
74                       eqz_preset_10b[i].f_amp[8],
75                       eqz_preset_10b[i].f_amp[9]];
76         [workValues addObject:workString];
77         [workPreamp addObject:[NSString stringWithFormat:@"%1.f", eqz_preset_10b[i].f_preamp]];
78         [workTitles addObject:toNSStr(preset_list_text[i])];
79         [workNames addObject:toNSStr(preset_list[i])];
80     }
82     NSString *defaultProfile = [NSString stringWithFormat:@"ZmxhdA==;;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i",
83                                 .0,25.,100.,-11.,8.,2.5,7.,.85,1.,.4,.5,.5,2.,0];
85     NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithArray:workValues], @"EQValues", [NSArray arrayWithArray:workPreamp], @"EQPreampValues", [NSArray arrayWithArray:workTitles], @"EQTitles", [NSArray arrayWithArray:workNames], @"EQNames", [NSArray arrayWithObject:defaultProfile], @"AudioEffectProfiles", [NSArray arrayWithObject:_NS("Default")], @"AudioEffectProfileNames", nil];
86     [defaults registerDefaults:appDefaults];
89 - (id)init
91     self = [super initWithWindowNibName:@"AudioEffects"];
92     if (self) {
93         i_old_profile_index = -1;
95         self.popupPanel = [[VLCPopupPanelController alloc] init];
96         self.textfieldPanel = [[VLCTextfieldPanelController alloc] init];
98         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AudioEffectApplyProfileOnStartup"])
99         {
100             [self equalizerUpdated];
101             [self resetCompressor];
102             [self resetSpatializer];
103             [self resetAudioFilters];
104             [self loadProfile];
105         }
106     }
108     return self;
111 - (void)loadProfile
113     intf_thread_t *p_intf = getIntf();
114     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
115     NSInteger selectedProfile = [defaults integerForKey:@"AudioEffectSelectedProfile"];
116     playlist_t *p_playlist = pl_Get(p_intf);
118     /* disable existing filters */
119     playlist_EnableAudioFilter(p_playlist, "equalizer", false);
120     playlist_EnableAudioFilter(p_playlist, "compressor", false);
121     playlist_EnableAudioFilter(p_playlist, "spatializer", false);
122     playlist_EnableAudioFilter(p_playlist, "compressor", false);
123     playlist_EnableAudioFilter(p_playlist, "headphone", false);
124     playlist_EnableAudioFilter(p_playlist, "normvol", false);
125     playlist_EnableAudioFilter(p_playlist, "karaoke", false);
127     /* fetch preset */
128     NSArray *items = [[[defaults objectForKey:@"AudioEffectProfiles"] objectAtIndex:(NSUInteger) selectedProfile] componentsSeparatedByString:@";"];
130     /* eq preset */
131     char const *psz_eq_preset = [B64DecNSStr([items firstObject]) UTF8String];
132     audio_output_t *p_aout = getAout();
133     if (p_aout)
134         var_SetString(p_aout, "equalizer-preset", psz_eq_preset);
135     var_SetString(p_playlist, "equalizer-preset", psz_eq_preset);
137     /* filter handling */
138     NSString *tempString = B64DecNSStr([items objectAtIndex:1]);
139     NSArray *tempArray;
140     NSUInteger count;
141     /* enable the new filters */
142     if ([tempString length] > 0) {
143         tempArray = [tempString componentsSeparatedByString:@":"];
144         count = [tempArray count];
145         for (NSUInteger x = 0; x < count; x++)
146             playlist_EnableAudioFilter(p_playlist, [[tempArray objectAtIndex:x] UTF8String], true);
147     }
149     /* values */
150     var_SetFloat(p_playlist, "compressor-rms-peak",[[items objectAtIndex:2] floatValue]);
151     var_SetFloat(p_playlist, "compressor-attack",[[items objectAtIndex:3] floatValue]);
152     var_SetFloat(p_playlist, "compressor-release",[[items objectAtIndex:4] floatValue]);
153     var_SetFloat(p_playlist, "compressor-threshold",[[items objectAtIndex:5] floatValue]);
154     var_SetFloat(p_playlist, "compressor-ratio",[[items objectAtIndex:6] floatValue]);
155     var_SetFloat(p_playlist, "compressor-knee",[[items objectAtIndex:7] floatValue]);
156     var_SetFloat(p_playlist, "compressor-makeup-gain",[[items objectAtIndex:8] floatValue]);
157     var_SetFloat(p_playlist, "spatializer-roomsize",[[items objectAtIndex:9] floatValue]);
158     var_SetFloat(p_playlist, "spatializer-width",[[items objectAtIndex:10] floatValue]);
159     var_SetFloat(p_playlist, "spatializer-wet",[[items objectAtIndex:11] floatValue]);
160     var_SetFloat(p_playlist, "spatializer-dry",[[items objectAtIndex:12] floatValue]);
161     var_SetFloat(p_playlist, "spatializer-damp",[[items objectAtIndex:13] floatValue]);
162     var_SetFloat(p_playlist, "norm-max-level",[[items objectAtIndex:14] floatValue]);
163     var_SetBool(p_playlist, "equalizer-2pass",(BOOL)[[items objectAtIndex:15] intValue]);
165     /* set values on-the-fly if we have an aout */
166     if (p_aout) {
167         var_SetFloat(p_aout, "compressor-rms-peak", [[items objectAtIndex:2] floatValue]);
168         var_SetFloat(p_aout, "compressor-attack", [[items objectAtIndex:3] floatValue]);
169         var_SetFloat(p_aout, "compressor-release", [[items objectAtIndex:4] floatValue]);
170         var_SetFloat(p_aout, "compressor-threshold", [[items objectAtIndex:5] floatValue]);
171         var_SetFloat(p_aout, "compressor-ratio", [[items objectAtIndex:6] floatValue]);
172         var_SetFloat(p_aout, "compressor-knee", [[items objectAtIndex:7] floatValue]);
173         var_SetFloat(p_aout, "compressor-makeup-gain", [[items objectAtIndex:8] floatValue]);
174         var_SetFloat(p_aout, "spatializer-roomsize", [[items objectAtIndex:9] floatValue]);
175         var_SetFloat(p_aout, "spatializer-width", [[items objectAtIndex:10] floatValue]);
176         var_SetFloat(p_aout, "spatializer-wet", [[items objectAtIndex:11] floatValue]);
177         var_SetFloat(p_aout, "spatializer-dry", [[items objectAtIndex:12] floatValue]);
178         var_SetFloat(p_aout, "spatializer-damp", [[items objectAtIndex:13] floatValue]);
179         var_SetFloat(p_aout, "norm-max-level", [[items objectAtIndex:14] floatValue]);
180         var_SetBool(p_aout, "equalizer-2pass", (BOOL)[[items objectAtIndex:15] intValue]);
181     }
183     /* update UI */
184     BOOL b_equalizerEnabled = [tempString rangeOfString:@"equalizer"].location != NSNotFound;
185     [_equalizerView enableSubviews:b_equalizerEnabled];
186     [_equalizerEnableCheckbox setState:(b_equalizerEnabled ? NSOnState : NSOffState)];
188     [_equalizerTwoPassCheckbox setState:[[items objectAtIndex:15] intValue]];
190     /* store current profile selection */
191     [defaults setInteger:selectedProfile forKey:@"AudioEffectSelectedProfile"];
192     [defaults synchronize];
194     if (p_aout)
195         vlc_object_release(p_aout);
198 - (void)windowDidLoad
200     [_applyProfileCheckbox setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"AudioEffectApplyProfileOnStartup"]];
202     /* setup the user's language */
203     /* Equalizer */
204     [_equalizerEnableCheckbox setTitle:_NS("Enable")];
205     [_equalizerTwoPassCheckbox setTitle:_NS("2 Pass")];
206     [_equalizerTwoPassCheckbox setToolTip:_NS("Filter the audio twice. This provides a more "  \
207                                               "intense effect.")];
208     [_equalizerPreampLabel setStringValue:_NS("Preamp")];
209     [_equalizerPreampLabel setToolTip:_NS("Set the global gain in dB (-20 ... 20).")];
211     /* Compressor */
212     [_compressorEnableCheckbox setTitle:_NS("Enable dynamic range compressor")];
213     [_compressorResetButton setTitle:_NS("Reset")];
214     [_compressorBand1Label setStringValue:_NS("RMS/peak")];;
215     [_compressorBand2Label setStringValue:_NS("Attack")];
216     [_compressorBand3Label setStringValue:_NS("Release")];
217     [_compressorBand4Label setStringValue:_NS("Threshold")];
218     [_compressorBand5Label setStringValue:_NS("Ratio")];
219     [_compressorBand6Label setStringValue:_NS("Knee radius")];
220     [_compressorBand7Label setStringValue:_NS("Makeup gain")];
222     /* Spatializer */
223     [_spatializerEnableCheckbox setTitle:_NS("Enable Spatializer")];
224     [_spatializerResetButton setTitle:_NS("Reset")];
225     [_spatializerBand1Label setStringValue:_NS("Size")];
226     [_spatializerBand1Label setToolTip:_NS("Defines the virtual surface of the room" \
227                                            " emulated by the filter.")];
228     [_spatializerBand2Label setStringValue:_NS("Width")];
229     [_spatializerBand2Label setToolTip:_NS("Width of the virtual room")];
230     [_spatializerBand3Label setStringValue:_NS("Wet")];
231     [_spatializerBand4Label setStringValue:_NS("Dry")];
232     [_spatializerBand5Label setStringValue:_NS("Damp")];
234     /* Filter */
235     [_filterHeadPhoneCheckbox setTitle:_NS("Headphone virtualization")];
236     [_filterHeadPhoneCheckbox setToolTip:_NS("This effect gives you the feeling that you are standing in a room " \
237                                              "with a complete 7.1 speaker set when using only a headphone, " \
238                                              "providing a more realistic sound experience. It should also be " \
239                                              "more comfortable and less tiring when listening to music for " \
240                                              "long periods of time.\nIt works with any source format from mono " \
241                                              "to 7.1.")];
242     [_filterNormLevelCheckbox setTitle:_NS("Volume normalization")];
243     [_filterNormLevelCheckbox setToolTip:_NS("Volume normalizer")];
244     [_filterNormLevelLabel setToolTip:_NS("If the average power over the last N buffers " \
245                                           "is higher than this value, the volume will be normalized. " \
246                                           "This value is a positive floating point number. A value " \
247                                           "between 0.5 and 10 seems sensible.")];
248     [_filterNormLevelLabel setStringValue:_NS("Maximum level")];
249     [_filterKaraokeCheckbox setTitle:_NS("Karaoke")];
250     [_filterKaraokeCheckbox setToolTip:_NS("Simple Karaoke filter")];
251     [_filterScaleTempoCheckbox setTitle:_NS("Scaletempo")];
252     [_filterScaleTempoCheckbox setToolTip:_NS("Audio tempo scaler synched with rate")];
253     [_filterStereoEnhancerCheckbox setTitle:_NS("Stereo Enhancer")];
254     [_filterStereoEnhancerCheckbox setToolTip:_NS("This filter enhances the stereo effect by "\
255                                                   "suppressing mono (signal common to both channels) "\
256                                                   "and by delaying the signal of left into right and vice versa, "\
257                                                   "thereby widening the stereo effect.")];
259     /* generic */
260     [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"equalizer"]] setLabel:_NS("Equalizer")];
261     [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"compressor"]] setLabel:_NS("Compressor")];
262     [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"spatializer"]] setLabel:_NS("Spatializer")];
263     [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"filter"]] setLabel:_NS("Filter")];
264     [self.window setTitle:_NS("Audio Effects")];
265     [self.window setExcludedFromWindowsMenu:YES];
266     [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
268     [self equalizerUpdated];
269     [self resetCompressor];
270     [self resetSpatializer];
271     [self resetAudioFilters];
272     [self resetProfileSelector];
275 #pragma mark -
276 #pragma mark internal functions
278 - (void)setAudioFilter: (char *)psz_name on:(BOOL)b_on
280     playlist_EnableAudioFilter(pl_Get(getIntf()), psz_name, b_on);
283 - (void)resetProfileSelector
285     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
286     [_profilePopup removeAllItems];
288     NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
289     [_profilePopup addItemsWithTitles:profileNames];
291     [[_profilePopup menu] addItem:[NSMenuItem separatorItem]];
292     [_profilePopup addItemWithTitle:_NS("Duplicate current profile...")];
293     [[_profilePopup lastItem] setTarget: self];
294     [[_profilePopup lastItem] setAction: @selector(addAudioEffectsProfile:)];
296     if ([profileNames count] > 1) {
297         [_profilePopup addItemWithTitle:_NS("Organize Profiles...")];
298         [[_profilePopup lastItem] setTarget: self];
299         [[_profilePopup lastItem] setAction: @selector(removeAudioEffectsProfile:)];
300     }
302     [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
303     if (i_old_profile_index || [[NSUserDefaults standardUserDefaults] integerForKey:@"AudioEffectSelectedProfile"])
304         [self profileSelectorAction:self];
307 #pragma mark -
308 #pragma mark generic code
309 - (void)updateCocoaWindowLevel:(NSInteger)i_level
311     if (self.isWindowLoaded && [self.window isVisible] && [self.window level] != i_level)
312         [self.window setLevel: i_level];
315 - (IBAction)toggleWindow:(id)sender
317     if ([self.window isKeyWindow])
318         [self.window orderOut:sender];
319     else {
320         [self.window setLevel: [[[VLCMain sharedInstance] voutController] currentStatusWindowLevel]];
321         [self.window makeKeyAndOrderFront:sender];
322     }
325 - (NSString *)generateProfileString
327     playlist_t *p_playlist = pl_Get(getIntf());
328     return [NSString stringWithFormat:@"%@;%@;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i",
329                      B64EncAndFree(var_GetNonEmptyString(p_playlist, "equalizer-preset")),
330                      B64EncAndFree(var_InheritString(p_playlist, "audio-filter")),
331                      var_InheritFloat(p_playlist, "compressor-rms-peak"),
332                      var_InheritFloat(p_playlist, "compressor-attack"),
333                      var_InheritFloat(p_playlist, "compressor-release"),
334                      var_InheritFloat(p_playlist, "compressor-threshold"),
335                      var_InheritFloat(p_playlist, "compressor-ratio"),
336                      var_InheritFloat(p_playlist, "compressor-knee"),
337                      var_InheritFloat(p_playlist, "compressor-makeup-gain"),
338                      var_InheritFloat(p_playlist, "spatializer-roomsize"),
339                      var_InheritFloat(p_playlist, "spatializer-width"),
340                      var_InheritFloat(p_playlist, "spatializer-wet"),
341                      var_InheritFloat(p_playlist, "spatializer-dry"),
342                      var_InheritFloat(p_playlist, "spatializer-damp"),
343                      var_InheritFloat(p_playlist, "norm-max-level"),
344                      var_InheritBool(p_playlist,"equalizer-2pass")];
347 - (void)saveCurrentProfile
349     if (i_old_profile_index == -1)
350         return;
352     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
353     /* fetch all the current settings in a uniform string */
354     NSString *newProfile = [self generateProfileString];
356     NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
357     if (i_old_profile_index >= [workArray count])
358         return;
360     [workArray replaceObjectAtIndex:i_old_profile_index withObject:newProfile];
361     [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
362     [defaults synchronize];
365 - (IBAction)profileSelectorAction:(id)sender
367     [self saveCurrentProfile];
368     i_old_profile_index = [_profilePopup indexOfSelectedItem];
369     [[NSUserDefaults standardUserDefaults] setInteger:i_old_profile_index forKey:@"AudioEffectSelectedProfile"];
370     [self loadProfile];
372     /* update UI */
373     [self resetCompressor];
374     [self resetSpatializer];
375     [self resetAudioFilters];
376     [self updatePresetSelector];
379 - (void)addAudioEffectsProfile:(id)sender
381     /* show panel */
382     [_textfieldPanel setTitleString:_NS("Duplicate current profile for a new profile")];
383     [_textfieldPanel setSubTitleString:_NS("Enter a name for the new profile:")];
384     [_textfieldPanel setCancelButtonString:_NS("Cancel")];
385     [_textfieldPanel setOkButtonString:_NS("Save")];
387     __weak typeof(self) _self = self;
388     [_textfieldPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSString *resultingText) {
390         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
391         if (returnCode != NSOKButton) {
392             [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
393             return;
394         }
396         NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
398         // duplicate names are not allowed in the popup control
399         if ([resultingText length] == 0 || [profileNames containsObject:resultingText]) {
400             [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
402             NSAlert *alert = [[NSAlert alloc] init];
403             [alert setAlertStyle:NSCriticalAlertStyle];
404             [alert setMessageText:_NS("Please enter a unique name for the new profile.")];
405             [alert setInformativeText:_NS("Multiple profiles with the same name are not allowed.")];
407             [alert beginSheetModalForWindow:_self.window
408                               modalDelegate:nil
409                              didEndSelector:nil
410                                 contextInfo:nil];
411             return;
412         }
414         NSString *newProfile = [_self generateProfileString];
416         /* add string to user defaults as well as a label */
417         NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
418         [workArray addObject:newProfile];
419         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
420         [defaults setInteger:[workArray count] - 1 forKey:@"AudioEffectSelectedProfile"];
421         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
422         [workArray addObject:resultingText];
423         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
425         /* save defaults */
426         [defaults synchronize];
427         [_self resetProfileSelector];
429     }];
432 - (void)removeAudioEffectsProfile:(id)sender
434     /* show panel */
435     [_popupPanel setTitleString:_NS("Remove a preset")];
436     [_popupPanel setSubTitleString:_NS("Select the preset you would like to remove:")];
437     [_popupPanel setOkButtonString:_NS("Remove")];
438     [_popupPanel setCancelButtonString:_NS("Cancel")];
439     [_popupPanel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"AudioEffectProfileNames"]];
441     __weak typeof(self) _self = self;
442     [_popupPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSInteger selectedIndex) {
444         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
445         if (returnCode != NSOKButton) {
446             [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
447             return;
448         }
450         /* remove selected profile from settings */
451         NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
452         [workArray removeObjectAtIndex:selectedIndex];
453         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
454         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
455         [workArray removeObjectAtIndex:selectedIndex];
456         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
458         if (i_old_profile_index >= selectedIndex)
459             [defaults setInteger:i_old_profile_index - 1 forKey:@"AudioEffectSelectedProfile"];
461         /* save defaults */
462         [defaults synchronize];
463         [_self resetProfileSelector];
464     }];
467 - (IBAction)applyProfileCheckboxChanged:(id)sender
469     [[NSUserDefaults standardUserDefaults] setBool:[sender state] forKey:@"AudioEffectApplyProfileOnStartup"];
472 #pragma mark -
473 #pragma mark Equalizer
474 static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
475                                char *psz_name)
477     char *psz_parser, *psz_string = NULL;
478     audio_output_t *p_aout = getAout();
479     if (!p_aout)
480         return false;
482     psz_string = var_InheritString(pl_Get(p_custom_intf), "audio-filter");
484     if (!psz_string)
485         psz_string = var_GetNonEmptyString(p_aout, "audio-filter");
487     vlc_object_release(p_aout);
489     if (!psz_string)
490         return false;
492     psz_parser = strstr(psz_string, psz_name);
494     free(psz_string);
496     if (psz_parser)
497         return true;
498     else
499         return false;
502 - (void)updatePresetSelector
504     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
505     NSArray *presets = [defaults objectForKey:@"EQNames"];
507     [_equalizerPresetsPopup removeAllItems];
508     [_equalizerPresetsPopup addItemsWithTitles:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
509     [[_equalizerPresetsPopup menu] addItem:[NSMenuItem separatorItem]];
510     [_equalizerPresetsPopup addItemWithTitle:_NS("Add new Preset...")];
511     [[_equalizerPresetsPopup lastItem] setTarget: self];
512     [[_equalizerPresetsPopup lastItem] setAction: @selector(addPresetAction:)];
514     if ([presets count] > 1) {
515         [_equalizerPresetsPopup addItemWithTitle:_NS("Organize Presets...")];
516         [[_equalizerPresetsPopup lastItem] setTarget: self];
517         [[_equalizerPresetsPopup lastItem] setAction: @selector(deletePresetAction:)];
518     }
520     audio_output_t *p_aout = getAout();
522     NSString *currentPreset = nil;
523     if (p_aout) {
524         char *psz_preset_string = var_GetNonEmptyString(p_aout, "equalizer-preset");
525         currentPreset = toNSStr(psz_preset_string);
526         free(psz_preset_string);
527         vlc_object_release(p_aout);
528     }
530     NSUInteger currentPresetIndex = 0;
531     if (currentPreset && [currentPreset length] > 0) {
532         currentPresetIndex = [presets indexOfObject:currentPreset];
534         if (currentPresetIndex == NSNotFound)
535             currentPresetIndex = [presets count] - 1;
536     }
538     [_equalizerPresetsPopup selectItemAtIndex:currentPresetIndex];
539     [self equalizerChangePreset:_equalizerPresetsPopup];
541     [_equalizerPreampSlider setFloatValue:[[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:currentPresetIndex] floatValue]];
542     [self setBandSliderValuesForPreset:currentPresetIndex];
545 - (void)equalizerUpdated
547     intf_thread_t *p_intf = getIntf();
548     playlist_t *p_playlist = pl_Get(p_intf);
549     bool b_2p = var_CreateGetBool(p_playlist, "equalizer-2pass");
550     bool bEnabled = GetEqualizerStatus(p_intf, (char *)"equalizer");
552     /* Setup sliders */
553     var_Create(p_playlist, "equalizer-preset",
554                VLC_VAR_STRING | VLC_VAR_DOINHERIT);
555     [self updatePresetSelector];
557     /* Set the the checkboxes */
558     [_equalizerView enableSubviews: bEnabled];
559     [_equalizerEnableCheckbox setState: bEnabled];
560     [_equalizerTwoPassCheckbox setState: b_2p];
563 - (id)sliderByIndex:(int)index
565     switch(index) {
566         case 0 : return _equalizerBand1Slider;
567         case 1 : return _equalizerBand2Slider;
568         case 2 : return _equalizerBand3Slider;
569         case 3 : return _equalizerBand4Slider;
570         case 4 : return _equalizerBand5Slider;
571         case 5 : return _equalizerBand6Slider;
572         case 6 : return _equalizerBand7Slider;
573         case 7 : return _equalizerBand8Slider;
574         case 8 : return _equalizerBand9Slider;
575         case 9 : return _equalizerBand10Slider;
576         default : return nil;
577     }
580 - (void)setBandSliderValuesForPreset:(NSInteger)presetID
582     NSString *preset = [[[NSUserDefaults standardUserDefaults] objectForKey:@"EQValues"] objectAtIndex:presetID];
583     NSArray *values = [preset componentsSeparatedByString:@" "];
584     NSUInteger count = [values count];
585     for (NSUInteger x = 0; x < count; x++)
586         [self setValue:[[values objectAtIndex:x] floatValue] forSlider:x];
589 - (NSString *)generatePresetString
591     return [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
592             [_equalizerBand1Slider floatValue],
593             [_equalizerBand2Slider floatValue],
594             [_equalizerBand3Slider floatValue],
595             [_equalizerBand4Slider floatValue],
596             [_equalizerBand5Slider floatValue],
597             [_equalizerBand6Slider floatValue],
598             [_equalizerBand7Slider floatValue],
599             [_equalizerBand8Slider floatValue],
600             [_equalizerBand9Slider floatValue],
601             [_equalizerBand10Slider floatValue]];
604 - (void)setValue:(float)value forSlider:(int)index
606     id slider = [self sliderByIndex:index];
608     if (slider != nil)
609         [slider setFloatValue:value];
612 - (IBAction)equalizerEnable:(id)sender
614     [_equalizerView enableSubviews:[sender state]];
615     [self setAudioFilter: "equalizer" on:[sender state]];
618 - (IBAction)equalizerBandSliderUpdated:(id)sender
620     audio_output_t *p_aout = getAout();
621     char const *psz_preset_values = [[self generatePresetString] UTF8String];
622     if (p_aout) {
623         var_SetString(p_aout, "equalizer-bands", psz_preset_values);
624         vlc_object_release(p_aout);
625     }
628 - (IBAction)equalizerChangePreset:(id)sender
630     NSInteger numberOfChosenPreset = [sender indexOfSelectedItem];
631     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
633     char const *psz_eq_bands = [[[defaults objectForKey:@"EQValues"] objectAtIndex:numberOfChosenPreset] UTF8String];
634     float f_eq_preamp = [[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:numberOfChosenPreset] floatValue];
635     char const *psz_eq_preset = [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String];
637     audio_output_t *p_aout = getAout();
638     if (p_aout) {
639         var_SetString(p_aout, "equalizer-bands", psz_eq_bands);
640         var_SetFloat(p_aout, "equalizer-preamp", f_eq_preamp);
641         var_SetString(p_aout, "equalizer-preset" , psz_eq_preset);
642         vlc_object_release(p_aout);
643     }
645     [_equalizerPreampSlider setFloatValue: f_eq_preamp];
646     [self setBandSliderValuesForPreset:numberOfChosenPreset];
648     var_SetString(pl_Get(getIntf()), "equalizer-preset", psz_eq_preset);
651 - (IBAction)equalizerPreAmpSliderUpdated:(id)sender
653     float fPreamp = [sender floatValue] ;
655     audio_output_t *p_aout = getAout();
656     if (p_aout) {
657         var_SetFloat(p_aout, "equalizer-preamp", fPreamp);
658         vlc_object_release(p_aout);
659     }
662 - (IBAction)equalizerTwoPass:(id)sender
664     bool b_2p = [sender state] ? true : false;
666     audio_output_t *p_aout = getAout();
667     if (p_aout) {
668         var_SetBool(p_aout, "equalizer-2pass", b_2p);
669         vlc_object_release(p_aout);
670     }
672     var_SetBool(pl_Get(getIntf()), "equalizer-2pass", b_2p);
675 - (IBAction)addPresetAction:(id)sender
677     /* show panel */
678     [_textfieldPanel setTitleString:_NS("Save current selection as new preset")];
679     [_textfieldPanel setSubTitleString:_NS("Enter a name for the new preset:")];
680     [_textfieldPanel setCancelButtonString:_NS("Cancel")];
681     [_textfieldPanel setOkButtonString:_NS("Save")];
683     __weak typeof(self) _self = self;
684     [_textfieldPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSString *resultingText) {
685         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
687         // EQ settings
688         if (returnCode != NSOKButton || [resultingText length] == 0)
689             return;
691         NSString *decomposedStringWithCanonicalMapping = [resultingText decomposedStringWithCanonicalMapping];
692         NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
693         [workArray addObject:[self generatePresetString]];
694         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
695         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
696         [workArray addObject:resultingText];
697         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
698         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
699         [workArray addObject:[NSString stringWithFormat:@"%.1f", [_equalizerPreampSlider floatValue]]];
700         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
701         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
702         [workArray addObject:decomposedStringWithCanonicalMapping];
703         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
704         [defaults synchronize];
706         /* update VLC internals */
707         char const *psz_eq_preset = [decomposedStringWithCanonicalMapping UTF8String];
708         audio_output_t *p_aout = getAout();
709         if (p_aout) {
710             var_SetString(p_aout, "equalizer-preset", psz_eq_preset);
711             vlc_object_release(p_aout);
712         }
714         var_SetString(pl_Get(getIntf()), "equalizer-preset", psz_eq_preset);
716         /* update UI */
717         [_self updatePresetSelector];
718     }];
721 - (IBAction)deletePresetAction:(id)sender
723     [_popupPanel setTitleString:_NS("Remove a preset")];
724     [_popupPanel setSubTitleString:_NS("Select the preset you would like to remove:")];
725     [_popupPanel setOkButtonString:_NS("Remove")];
726     [_popupPanel setCancelButtonString:_NS("Cancel")];
727     [_popupPanel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
729     __weak typeof(self) _self = self;
730     [_popupPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSInteger selectedIndex) {
732         if (returnCode != NSOKButton)
733             return;
735         /* remove requested profile from the arrays */
736         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
737         NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
738         [workArray removeObjectAtIndex:selectedIndex];
739         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
740         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
741         [workArray removeObjectAtIndex:selectedIndex];
742         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
743         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
744         [workArray removeObjectAtIndex:selectedIndex];
745         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
746         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
747         [workArray removeObjectAtIndex:selectedIndex];
748         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
749         [defaults synchronize];
751         /* update UI */
752         [_self updatePresetSelector];
753     }];
756 #pragma mark -
757 #pragma mark Compressor
758 - (void)resetCompressor
760     intf_thread_t *p_intf = getIntf();
761     playlist_t *p_playlist = pl_Get(p_intf);
762     BOOL bEnable_compressor = NO;
763     char *psz_afilters;
764     psz_afilters = var_InheritString(p_playlist, "audio-filter");
765     if (psz_afilters) {
766         bEnable_compressor = strstr(psz_afilters, "compressor") != NULL;
767         [_compressorEnableCheckbox setState: (NSInteger)strstr(psz_afilters, "compressor") ];
768         free(psz_afilters);
769     }
771     [_compressorView enableSubviews:bEnable_compressor];
772     [_compressorEnableCheckbox setState:(bEnable_compressor ? NSOnState : NSOffState)];
774     [_compressorBand1Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-rms-peak")];
775     [_compressorBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [_compressorBand1Slider floatValue]]];
776     [_compressorBand2Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-attack")];
777     [_compressorBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [_compressorBand2Slider floatValue]]];
778     [_compressorBand3Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-release")];
779     [_compressorBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [_compressorBand3Slider floatValue]]];
780     [_compressorBand4Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-threshold")];
781     [_compressorBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [_compressorBand4Slider floatValue]]];
782     [_compressorBand5Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-ratio")];
783     [_compressorBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [_compressorBand5Slider floatValue]]];
784     [_compressorBand6Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-knee")];
785     [_compressorBand6TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [_compressorBand6Slider floatValue]]];
786     [_compressorBand7Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-makeup-gain")];
787     [_compressorBand7TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [_compressorBand7Slider floatValue]]];
790 - (IBAction)resetCompressorValues:(id)sender
792     playlist_t *p_playlist = pl_Get(getIntf());
793     var_SetFloat(p_playlist, "compressor-rms-peak", 0.000000);
794     var_SetFloat(p_playlist, "compressor-attack", 25.000000);
795     var_SetFloat(p_playlist, "compressor-release", 100.000000);
796     var_SetFloat(p_playlist, "compressor-threshold", -11.000000);
797     var_SetFloat(p_playlist, "compressor-ratio", 8.000000);
798     var_SetFloat(p_playlist, "compressor-knee", 2.500000);
799     var_SetFloat(p_playlist, "compressor-makeup-gain", 7.000000);
801     audio_output_t *p_aout = getAout();
802     if (p_aout) {
803         var_SetFloat(p_aout, "compressor-rms-peak", 0.000000);
804         var_SetFloat(p_aout, "compressor-attack", 25.000000);
805         var_SetFloat(p_aout, "compressor-release", 100.000000);
806         var_SetFloat(p_aout, "compressor-threshold", -11.000000);
807         var_SetFloat(p_aout, "compressor-ratio", 8.000000);
808         var_SetFloat(p_aout, "compressor-knee", 2.500000);
809         var_SetFloat(p_aout, "compressor-makeup-gain", 7.000000);
810         vlc_object_release(p_aout);
811     }
812     [self resetCompressor];
815 - (IBAction)compressorEnable:(id)sender
817     [_compressorView enableSubviews:[sender state]];
818     [self setAudioFilter:"compressor" on:[sender state]];
821 - (IBAction)compressorSliderUpdated:(id)sender
823     audio_output_t *p_aout = getAout();
824     char *psz_property;
825     float f_value = [sender floatValue];
827     if (sender == _compressorBand1Slider)
828         psz_property = "compressor-rms-peak";
829     else if (sender == _compressorBand2Slider)
830         psz_property = "compressor-attack";
831     else if (sender == _compressorBand3Slider)
832         psz_property = "compressor-release";
833     else if (sender == _compressorBand4Slider)
834         psz_property = "compressor-threshold";
835     else if (sender == _compressorBand5Slider)
836         psz_property = "compressor-ratio";
837     else if (sender == _compressorBand6Slider)
838         psz_property = "compressor-knee";
839     else if (sender == _compressorBand7Slider)
840         psz_property = "compressor-makeup-gain";
842     if (p_aout) {
843         var_SetFloat(p_aout, psz_property, f_value);
844         vlc_object_release(p_aout);
845     }
846     var_SetFloat(pl_Get(getIntf()), psz_property, f_value);
848     if (sender == _compressorBand1Slider)
849         [_compressorBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
850     else if (sender == _compressorBand2Slider)
851         [_compressorBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", f_value]];
852     else if (sender == _compressorBand3Slider)
853         [_compressorBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", f_value]];
854     else if (sender == _compressorBand4Slider)
855         [_compressorBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", f_value]];
856     else if (sender == _compressorBand5Slider)
857         [_compressorBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", f_value]];
858     else if (sender == _compressorBand6Slider)
859         [_compressorBand6TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", f_value]];
860     else if (sender == _compressorBand7Slider)
861         [_compressorBand7TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", f_value]];
864 #pragma mark -
865 #pragma mark Spatializer
866 - (void)resetSpatializer
868     playlist_t *p_playlist = pl_Get(getIntf());
869     BOOL bEnable_spatializer = NO;
870     char *psz_afilters;
871     psz_afilters = var_InheritString(p_playlist, "audio-filter");
872     if (psz_afilters) {
873         bEnable_spatializer = strstr(psz_afilters, "spatializer") != NULL;
874         free(psz_afilters);
875     }
877     [_spatializerView enableSubviews:bEnable_spatializer];
878     [_spatializerEnableCheckbox setState:(bEnable_spatializer ? NSOnState : NSOffState)];
881 #define setSlider(bandsld, bandfld, var) \
882 [bandsld setFloatValue: var_CreateGetFloat(p_playlist, var) * 10.]; \
883 [bandfld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [bandsld floatValue]]]
885     setSlider(_spatializerBand1Slider, _spatializerBand1TextField, "spatializer-roomsize");
886     setSlider(_spatializerBand2Slider, _spatializerBand2TextField, "spatializer-width");
887     setSlider(_spatializerBand3Slider, _spatializerBand3TextField, "spatializer-wet");
888     setSlider(_spatializerBand4Slider, _spatializerBand4TextField, "spatializer-dry");
889     setSlider(_spatializerBand5Slider, _spatializerBand5TextField, "spatializer-damp");
891 #undef setSlider
894 - (IBAction)resetSpatializerValues:(id)sender
896     playlist_t *p_playlist = pl_Get(getIntf());
897     var_SetFloat(p_playlist, "spatializer-roomsize", .85);
898     var_SetFloat(p_playlist, "spatializer-width", 1.);
899     var_SetFloat(p_playlist, "spatializer-wet", .4);
900     var_SetFloat(p_playlist, "spatializer-dry", .5);
901     var_SetFloat(p_playlist, "spatializer-damp", .5);
903     audio_output_t *p_aout = getAout();
904     if (p_aout) {
905         var_SetFloat(p_aout, "spatializer-roomsize", .85);
906         var_SetFloat(p_aout, "spatializer-width", 1.);
907         var_SetFloat(p_aout, "spatializer-wet", .4);
908         var_SetFloat(p_aout, "spatializer-dry", .5);
909         var_SetFloat(p_aout, "spatializer-damp", .5);
910         vlc_object_release(p_aout);
911     }
912     [self resetSpatializer];
915 - (IBAction)spatializerEnable:(id)sender
917     [_spatializerView enableSubviews:[sender state]];
918     [self setAudioFilter:"spatializer" on:[sender state]];
921 - (IBAction)spatializerSliderUpdated:(id)sender
923     audio_output_t *p_aout = getAout();
924     char *psz_property = NULL;
925     float f_value = [sender floatValue];
927     if (sender == _spatializerBand1Slider)
928         psz_property = "spatializer-roomsize";
929     else if (sender == _spatializerBand2Slider)
930         psz_property = "spatializer-width";
931     else if (sender == _spatializerBand3Slider)
932         psz_property = "spatializer-wet";
933     else if (sender == _spatializerBand4Slider)
934         psz_property = "spatializer-dry";
935     else if (sender == _spatializerBand5Slider)
936         psz_property = "spatializer-damp";
938     if (p_aout) {
939         var_SetFloat(p_aout, psz_property, f_value / 10.f);
940         vlc_object_release(p_aout);
941     }
942     var_SetFloat(pl_Get(getIntf()), psz_property, f_value / 10.f);
944     if (sender == _spatializerBand1Slider)
945         [_spatializerBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
946     else if (sender == _spatializerBand2Slider)
947         [_spatializerBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
948     else if (sender == _spatializerBand3Slider)
949         [_spatializerBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
950     else if (sender == _spatializerBand4Slider)
951         [_spatializerBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
952     else if (sender == _spatializerBand5Slider)
953         [_spatializerBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
956 #pragma mark -
957 #pragma mark Filter
958 - (void)resetAudioFilters
960     playlist_t *p_playlist = pl_Get(getIntf());
961     BOOL bEnable_normvol = NO;
962     char *psz_afilters;
963     psz_afilters = var_InheritString(p_playlist, "audio-filter");
964     if (psz_afilters) {
965         [_filterHeadPhoneCheckbox setState: (NSInteger)strstr(psz_afilters, "headphone") ];
966         [_filterKaraokeCheckbox setState: (NSInteger)strstr(psz_afilters, "karaoke") ];
967         [_filterScaleTempoCheckbox setState: (NSInteger)strstr(psz_afilters, "scaletempo") ];
968         [_filterStereoEnhancerCheckbox setState: (NSInteger)strstr(psz_afilters, "stereo_widen") ];
969         bEnable_normvol = strstr(psz_afilters, "normvol") != NULL;
970         free(psz_afilters);
971     } else {
972         [_filterHeadPhoneCheckbox setState: NSOffState];
973         [_filterKaraokeCheckbox setState: NSOffState];
974         [_filterScaleTempoCheckbox setState: NSOffState];
975         [_filterStereoEnhancerCheckbox setState: NSOffState];
976     }
978     [_filterNormLevelSlider setEnabled:bEnable_normvol];
979     [_filterNormLevelLabel setEnabled:bEnable_normvol];
980     [_filterNormLevelCheckbox setState:(bEnable_normvol ? NSOnState : NSOffState)];
982     [_filterNormLevelSlider setFloatValue: var_CreateGetFloat(p_playlist, "norm-max-level")];
985 - (IBAction)filterEnableHeadPhoneVirt:(id)sender
987     [self setAudioFilter:"headphone" on:[sender state]];
990 - (IBAction)filterEnableVolumeNorm:(id)sender
992     [_filterNormLevelSlider setEnabled:[sender state]];
993     [_filterNormLevelLabel setEnabled:[sender state]];
994     [self setAudioFilter:"normvol" on:[sender state]];
997 - (IBAction)filterVolumeNormSliderUpdated:(id)sender
999     audio_output_t *p_aout = getAout();
1000     float f_value = [_filterNormLevelSlider floatValue];
1002     if (p_aout) {
1003         var_SetFloat(p_aout, "norm-max-level", f_value);
1004         vlc_object_release(p_aout);
1005     }
1007     var_SetFloat(pl_Get(getIntf()), "norm-max-level", f_value);
1010 - (IBAction)filterEnableKaraoke:(id)sender
1012     [self setAudioFilter:"karaoke" on:[sender state]];
1015 - (IBAction)filterEnableScaleTempo:(id)sender
1017     [self setAudioFilter:"scaletempo" on:[sender state]];
1020 - (IBAction)filterEnableStereoEnhancer:(id)sender
1022     [self setAudioFilter:"stereo_widen" on:[sender state]];
1025 @end