]> git.sesse.net Git - vlc/blob - modules/gui/macosx/AudioEffects.m
macosx: fix moar string memleaks in audio effects code
[vlc] / modules / gui / macosx / AudioEffects.m
1 /*****************************************************************************
2  * AudioEffects.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2004-2012 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  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # import "config.h"
27 #endif
28
29 #import "intf.h"
30 #import "AudioEffects.h"
31 #import "../../audio_filter/equalizer_presets.h"
32 #import "CompatibilityFixes.h"
33 #import "SharedDialogs.h"
34
35 #import <vlc_common.h>
36
37 #import <math.h>
38
39 @interface VLCAudioEffects (Internal)
40 - (void)resetProfileSelector;
41 - (void)updatePresetSelector;
42 - (void)setBandSliderValuesForPreset:(NSInteger)presetID;
43 @end
44
45 #pragma mark -
46 #pragma mark Initialization
47
48 @implementation VLCAudioEffects
49 static VLCAudioEffects *_o_sharedInstance = nil;
50
51 + (VLCAudioEffects *)sharedInstance
52 {
53     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
54 }
55
56 + (void)initialize{
57     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
58
59     NSString *workString;
60     NSMutableArray *workValues = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
61     NSMutableArray *workPreamp = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
62     NSMutableArray *workTitles = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
63     NSMutableArray *workNames = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
64
65     for (int i = 0 ; i < NB_PRESETS ; i++) {
66         workString = [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
67                       eqz_preset_10b[i].f_amp[0],
68                       eqz_preset_10b[i].f_amp[1],
69                       eqz_preset_10b[i].f_amp[2],
70                       eqz_preset_10b[i].f_amp[3],
71                       eqz_preset_10b[i].f_amp[4],
72                       eqz_preset_10b[i].f_amp[5],
73                       eqz_preset_10b[i].f_amp[6],
74                       eqz_preset_10b[i].f_amp[7],
75                       eqz_preset_10b[i].f_amp[8],
76                       eqz_preset_10b[i].f_amp[9]];
77         [workValues addObject:workString];
78         [workPreamp addObject:[NSString stringWithFormat:@"%1.f", eqz_preset_10b[i].f_preamp]];
79         [workTitles addObject:@(preset_list_text[i])];
80         [workNames addObject:@(preset_list[i])];
81     }
82
83     NSString *defaultProfile = [NSString stringWithFormat:@"ZmxhdA==;;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i",
84                                 .0,25.,100.,-11.,8.,2.5,7.,.85,1.,.4,.5,.5,2.,0];
85
86     NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithArray:workValues], @"EQValues", [NSArray arrayWithArray:workPreamp], @"EQPreampValues", [NSArray arrayWithArray:workTitles], @"EQTitles", [NSArray arrayWithArray:workNames], @"EQNames", @[defaultProfile], @"AudioEffectProfiles", @[_NS("Default")], @"AudioEffectProfileNames", nil];
87     [defaults registerDefaults:appDefaults];
88
89     [workValues release];
90     [workPreamp release];
91     [workTitles release];
92     [workNames release];
93 }
94
95 - (id)init
96 {
97     if (_o_sharedInstance)
98         [self dealloc];
99     else {
100         p_intf = VLCIntf;
101         i_old_profile_index = -1;
102         _o_sharedInstance = [super init];
103     }
104
105     return _o_sharedInstance;
106 }
107
108 - (void)awakeFromNib
109 {
110     /* setup the user's language */
111     /* Equalizer */
112     [o_eq_enable_ckb setTitle:_NS("Enable")];
113     [o_eq_twopass_ckb setTitle:_NS("2 Pass")];
114     [o_eq_preamp_lbl setStringValue:_NS("Preamp")];
115
116     /* Compressor */
117     [o_comp_enable_ckb setTitle:_NS("Enable dynamic range compressor")];
118     [o_comp_reset_btn setTitle:_NS("Reset")];
119     [o_comp_band1_lbl setStringValue:_NS("RMS/peak")];;
120     [o_comp_band2_lbl setStringValue:_NS("Attack")];
121     [o_comp_band3_lbl setStringValue:_NS("Release")];
122     [o_comp_band4_lbl setStringValue:_NS("Threshold")];
123     [o_comp_band5_lbl setStringValue:_NS("Ratio")];
124     [o_comp_band6_lbl setStringValue:_NS("Knee radius")];
125     [o_comp_band7_lbl setStringValue:_NS("Makeup gain")];
126
127     /* Spatializer */
128     [o_spat_enable_ckb setTitle:_NS("Enable Spatializer")];
129     [o_spat_reset_btn setTitle:_NS("Reset")];
130     [o_spat_band1_lbl setStringValue:_NS("Size")];
131     [o_spat_band2_lbl setStringValue:_NS("Width")];
132     [o_spat_band3_lbl setStringValue:_NS("Wet")];
133     [o_spat_band4_lbl setStringValue:_NS("Dry")];
134     [o_spat_band5_lbl setStringValue:_NS("Damp")];
135
136     /* Filter */
137     [o_filter_headPhone_ckb setTitle:_NS("Headphone virtualization")];
138     [o_filter_normLevel_ckb setTitle:_NS("Volume normalization")];
139     [o_filter_normLevel_lbl setStringValue:_NS("Maximum level")];
140     [o_filter_karaoke_ckb setTitle:_NS("Karaoke")];
141
142     /* generic */
143     [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"equalizer"]] setLabel:_NS("Equalizer")];
144     [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"compressor"]] setLabel:_NS("Compressor")];
145     [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"spatializer"]] setLabel:_NS("Spatializer")];
146     [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"filter"]] setLabel:_NS("Filter")];
147     [o_window setTitle:_NS("Audio Effects")];
148     [o_window setExcludedFromWindowsMenu:YES];
149     if (!OSX_SNOW_LEOPARD)
150         [o_window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
151
152     [self setupEqualizer];
153     [self resetCompressor];
154     [self resetSpatializer];
155     [self resetAudioFilters];
156     [self resetProfileSelector];
157 }
158
159 #pragma mark -
160 #pragma mark internal functions
161
162 - (void)setAudioFilter: (char *)psz_name on:(BOOL)b_on
163 {
164     char *psz_tmp;
165     audio_output_t *p_aout = getAout();
166     if (p_aout)
167         psz_tmp = var_GetNonEmptyString(p_aout, "audio-filter");
168     else
169         psz_tmp = config_GetPsz(p_intf, "audio-filter");
170
171     if (b_on) {
172         if (!psz_tmp)
173             config_PutPsz(p_intf, "audio-filter", psz_name);
174         else if (strstr(psz_tmp, psz_name) == NULL) {
175             psz_tmp = (char *)[[NSString stringWithFormat: @"%s:%s", psz_tmp, psz_name] UTF8String];
176             config_PutPsz(p_intf, "audio-filter", psz_tmp);
177         }
178     } else {
179         if (psz_tmp) {
180             psz_tmp = (char *)[[@(psz_tmp) stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@":%s",psz_name]]] UTF8String];
181             psz_tmp = (char *)[[@(psz_tmp) stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@"%s:",psz_name]]] UTF8String];
182             psz_tmp = (char *)[[@(psz_tmp) stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@(psz_name)]] UTF8String];
183             config_PutPsz(p_intf, "audio-filter", psz_tmp);
184         }
185     }
186
187     if (p_aout) {
188         playlist_EnableAudioFilter(pl_Get(p_intf), psz_name, b_on);
189         vlc_object_release(p_aout);
190     }
191 }
192
193 - (void)resetProfileSelector
194 {
195     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
196     [o_profile_pop removeAllItems];
197
198     NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
199     [o_profile_pop addItemsWithTitles:profileNames];
200
201     [[o_profile_pop menu] addItem:[NSMenuItem separatorItem]];
202     [o_profile_pop addItemWithTitle:_NS("Duplicate current profile...")];
203     [[o_profile_pop lastItem] setTarget: self];
204     [[o_profile_pop lastItem] setAction: @selector(addAudioEffectsProfile:)];
205
206     if ([profileNames count] > 1) {
207         [o_profile_pop addItemWithTitle:_NS("Organize Profiles...")];
208         [[o_profile_pop lastItem] setTarget: self];
209         [[o_profile_pop lastItem] setAction: @selector(removeAudioEffectsProfile:)];
210     }
211
212     [o_profile_pop selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
213     [self profileSelectorAction:self];
214 }
215
216 #pragma mark -
217 #pragma mark generic code
218 - (void)updateCocoaWindowLevel:(NSInteger)i_level
219 {
220     if (o_window && [o_window isVisible] && [o_window level] != i_level)
221         [o_window setLevel: i_level];
222 }
223
224 - (IBAction)toggleWindow:(id)sender
225 {
226     if ([o_window isVisible])
227         [o_window orderOut:sender];
228     else {
229         [o_window setLevel: [[[VLCMain sharedInstance] voutController] currentWindowLevel]];
230         [o_window makeKeyAndOrderFront:sender];
231     }
232 }
233
234 - (NSString *)generateProfileString
235 {
236     vlc_object_t *p_object = VLC_OBJECT(getAout());
237     if (p_object == NULL)
238         p_object = vlc_object_hold(pl_Get(p_intf));
239
240     NSString *o_str = [NSString stringWithFormat:@"%@;%@;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%lli",
241                        B64EncAndFree(var_GetNonEmptyString(p_object, "equalizer-preset")),
242                        B64EncAndFree(config_GetPsz(p_intf, "audio-filter")),
243                        config_GetFloat(p_intf, "compressor-rms-peak"),
244                        config_GetFloat(p_intf, "compressor-attack"),
245                        config_GetFloat(p_intf, "compressor-release"),
246                        config_GetFloat(p_intf, "compressor-threshold"),
247                        config_GetFloat(p_intf, "compressor-ratio"),
248                        config_GetFloat(p_intf, "compressor-knee"),
249                        config_GetFloat(p_intf, "compressor-makeup-gain"),
250                        config_GetFloat(p_intf, "spatializer-roomsize"),
251                        config_GetFloat(p_intf, "spatializer-width"),
252                        config_GetFloat(p_intf, "spatializer-wet"),
253                        config_GetFloat(p_intf, "spatializer-dry"),
254                        config_GetFloat(p_intf, "spatializer-damp"),
255                        config_GetFloat(p_intf, "norm-max-level"),
256                        config_GetInt(p_intf,"equalizer-2pass")];
257
258     vlc_object_release(p_object);
259     return o_str;
260 }
261
262 - (void)saveCurrentProfile
263 {
264     if (i_old_profile_index == -1)
265         return;
266
267     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
268     /* fetch all the current settings in a uniform string */
269     NSString *newProfile = [self generateProfileString];
270
271     NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
272     if (i_old_profile_index >= [workArray count])
273         return;
274
275     [workArray replaceObjectAtIndex:i_old_profile_index withObject:newProfile];
276     [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
277     [workArray release];
278     [defaults synchronize];
279 }
280
281 - (IBAction)profileSelectorAction:(id)sender
282 {
283     [self saveCurrentProfile];
284     i_old_profile_index = [o_profile_pop indexOfSelectedItem];
285
286     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
287     NSUInteger selectedProfile = [o_profile_pop indexOfSelectedItem];
288
289     audio_output_t *p_aout = getAout();
290     playlist_t *p_playlist = pl_Get(p_intf);
291
292     if (p_aout) {
293         /* disable existing filters */
294         playlist_EnableAudioFilter(p_playlist, "equalizer", false);
295         playlist_EnableAudioFilter(p_playlist, "compressor", false);
296         playlist_EnableAudioFilter(p_playlist, "spatializer", false);
297         playlist_EnableAudioFilter(p_playlist, "compressor", false);
298         playlist_EnableAudioFilter(p_playlist, "headphone", false);
299         playlist_EnableAudioFilter(p_playlist, "normvol", false);
300         playlist_EnableAudioFilter(p_playlist, "karaoke", false);
301     }
302
303     /* fetch preset */
304     NSArray *items = [[[defaults objectForKey:@"AudioEffectProfiles"] objectAtIndex:selectedProfile] componentsSeparatedByString:@";"];
305
306     /* eq preset */
307     vlc_object_t *p_object = VLC_OBJECT(getAout());
308     if (p_object == NULL)
309         p_object = vlc_object_hold(pl_Get(p_intf));
310     var_SetString(p_object, "equalizer-preset", [B64DecNSStr([items objectAtIndex:0]) UTF8String]);
311     vlc_object_release(p_object);
312
313     /* filter handling */
314     NSString *tempString = B64DecNSStr([items objectAtIndex:1]);
315     NSArray *tempArray;
316     NSUInteger count;
317     /* enable the new filters, if we have an aout */
318     if (p_aout) {
319         if ([tempString length] > 0) {
320             tempArray = [tempString componentsSeparatedByString:@":"];
321             count = [tempArray count];
322             for (NSUInteger x = 0; x < count; x++)
323                 playlist_EnableAudioFilter(p_playlist, (char *)[[tempArray objectAtIndex:x] UTF8String], true);
324         }
325     }
326     config_PutPsz(p_intf,"audio-filter",[tempString UTF8String]);
327
328     /* values */
329     config_PutFloat(p_intf, "compressor-rms-peak",[[items objectAtIndex:2] floatValue]);
330     config_PutFloat(p_intf, "compressor-attack",[[items objectAtIndex:3] floatValue]);
331     config_PutFloat(p_intf, "compressor-release",[[items objectAtIndex:4] floatValue]);
332     config_PutFloat(p_intf, "compressor-threshold",[[items objectAtIndex:5] floatValue]);
333     config_PutFloat(p_intf, "compressor-ratio",[[items objectAtIndex:6] floatValue]);
334     config_PutFloat(p_intf, "compressor-knee",[[items objectAtIndex:7] floatValue]);
335     config_PutFloat(p_intf, "compressor-makeup-gain",[[items objectAtIndex:8] floatValue]);
336     config_PutFloat(p_intf, "spatializer-roomsize",[[items objectAtIndex:9] floatValue]);
337     config_PutFloat(p_intf, "spatializer-width",[[items objectAtIndex:10] floatValue]);
338     config_PutFloat(p_intf, "spatializer-wet",[[items objectAtIndex:11] floatValue]);
339     config_PutFloat(p_intf, "spatializer-dry",[[items objectAtIndex:12] floatValue]);
340     config_PutFloat(p_intf, "spatializer-damp",[[items objectAtIndex:13] floatValue]);
341     config_PutFloat(p_intf, "norm-max-level",[[items objectAtIndex:14] floatValue]);
342     config_PutInt(p_intf, "equalizer-2pass",[[items objectAtIndex:15] intValue]);
343
344     /* set values on-the-fly if we have an aout */
345     if (p_aout) {
346         var_SetFloat(p_aout, "compressor-rms-peak", [[items objectAtIndex:2] floatValue]);
347         var_SetFloat(p_aout, "compressor-attack", [[items objectAtIndex:3] floatValue]);
348         var_SetFloat(p_aout, "compressor-release", [[items objectAtIndex:4] floatValue]);
349         var_SetFloat(p_aout, "compressor-threshold", [[items objectAtIndex:5] floatValue]);
350         var_SetFloat(p_aout, "compressor-ratio", [[items objectAtIndex:6] floatValue]);
351         var_SetFloat(p_aout, "compressor-knee", [[items objectAtIndex:7] floatValue]);
352         var_SetFloat(p_aout, "compressor-makeup-gain", [[items objectAtIndex:8] floatValue]);
353         var_SetFloat(p_aout, "spatializer-roomsize", [[items objectAtIndex:9] floatValue]);
354         var_SetFloat(p_aout, "spatializer-width", [[items objectAtIndex:10] floatValue]);
355         var_SetFloat(p_aout, "spatializer-wet", [[items objectAtIndex:11] floatValue]);
356         var_SetFloat(p_aout, "spatializer-dry", [[items objectAtIndex:12] floatValue]);
357         var_SetFloat(p_aout, "spatializer-damp", [[items objectAtIndex:13] floatValue]);
358         var_SetFloat(p_aout, "norm-max-level", [[items objectAtIndex:14] floatValue]);
359         var_SetBool(p_aout, "equalizer-2pass", (BOOL)[[items objectAtIndex:15] intValue]);
360     }
361
362     /* update UI */
363     if ([tempString rangeOfString:@"equalizer"].location == NSNotFound)
364         [o_eq_enable_ckb setState:NSOffState];
365     else
366         [o_eq_enable_ckb setState:NSOnState];
367     [o_eq_twopass_ckb setState:[[items objectAtIndex:15] intValue]];
368     [self resetCompressor];
369     [self resetSpatializer];
370     [self resetAudioFilters];
371     [self updatePresetSelector];
372
373     /* store current profile selection */
374     [defaults setInteger:selectedProfile forKey:@"AudioEffectSelectedProfile"];
375     [defaults synchronize];
376
377     if (p_aout)
378         vlc_object_release(p_aout);
379 }
380
381 - (IBAction)addAudioEffectsProfile:(id)sender
382 {
383     /* show panel */
384     VLCEnterTextPanel *panel = [VLCEnterTextPanel sharedInstance];
385     [panel setTitle: _NS("Duplicate current profile for a new profile")];
386     [panel setSubTitle: _NS("Enter a name for the new profile:")];
387     [panel setCancelButtonLabel: _NS("Cancel")];
388     [panel setOKButtonLabel: _NS("Save")];
389     [panel setTarget:self];
390     b_genericAudioProfileInInteraction = YES;
391
392     [panel runModalForWindow:o_window];
393 }
394
395 - (IBAction)removeAudioEffectsProfile:(id)sender
396 {
397     /* show panel */
398     VLCSelectItemInPopupPanel *panel = [VLCSelectItemInPopupPanel sharedInstance];
399     [panel setTitle:_NS("Remove a preset")];
400     [panel setSubTitle:_NS("Select the preset you would like to remove:")];
401     [panel setOKButtonLabel:_NS("Remove")];
402     [panel setCancelButtonLabel:_NS("Cancel")];
403     [panel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"AudioEffectProfileNames"]];
404     [panel setTarget:self];
405     b_genericAudioProfileInInteraction = YES;
406
407     [panel runModalForWindow:o_window];
408 }
409
410 #pragma mark -
411 #pragma mark Equalizer
412 static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
413                                char *psz_name)
414 {
415     char *psz_parser, *psz_string = NULL;
416     audio_output_t *p_aout = getAout();
417     if (!p_aout)
418         return false;
419
420     psz_string = config_GetPsz(p_custom_intf, "audio-filter");
421
422     if (!psz_string)
423         psz_string = var_GetNonEmptyString(p_aout, "audio-filter");
424
425     vlc_object_release(p_aout);
426
427     if (!psz_string)
428         return false;
429
430     psz_parser = strstr(psz_string, psz_name);
431
432     free(psz_string);
433
434     if (psz_parser)
435         return true;
436     else
437         return false;
438 }
439
440 - (void)setupEqualizer
441 {
442     audio_output_t *p_aout = getAout();
443     if (p_aout) {
444         var_Create(p_aout, "equalizer-preset", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
445         var_Create(p_aout, "equalizer-preamp", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
446         var_Create(p_aout, "equalizer-bands", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
447         vlc_object_release(p_aout);
448     }
449
450     [self equalizerUpdated];
451 }
452
453 - (void)updatePresetSelector
454 {
455     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
456     NSArray *presets = [defaults objectForKey:@"EQNames"];
457
458     [o_eq_presets_popup removeAllItems];
459     [o_eq_presets_popup addItemsWithTitles:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
460     [[o_eq_presets_popup menu] addItem:[NSMenuItem separatorItem]];
461     [o_eq_presets_popup addItemWithTitle:_NS("Add new Preset...")];
462     [[o_eq_presets_popup lastItem] setTarget: self];
463     [[o_eq_presets_popup lastItem] setAction: @selector(addPresetAction:)];
464
465     if ([presets count] > 1) {
466         [o_eq_presets_popup addItemWithTitle:_NS("Organize Presets...")];
467         [[o_eq_presets_popup lastItem] setTarget: self];
468         [[o_eq_presets_popup lastItem] setAction: @selector(deletePresetAction:)];
469     }
470
471     audio_output_t *p_aout = getAout();
472
473     NSString *currentPreset = nil;
474     if (p_aout) {
475         char *psz_preset_string = var_GetNonEmptyString(p_aout, "equalizer-preset");
476         currentPreset = [NSString stringWithFormat:@"%s", psz_preset_string];
477         free(psz_preset_string);
478         vlc_object_release(p_aout);
479     }
480
481     NSUInteger currentPresetIndex = 0;
482     if (currentPreset && [currentPreset length] > 0) {
483         currentPresetIndex = [presets indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
484             return [obj isEqualToString:currentPreset];
485         }];
486
487         if (currentPresetIndex == NSNotFound)
488             currentPresetIndex = [presets count] - 1;
489     }    
490
491     [o_eq_presets_popup selectItemAtIndex:currentPresetIndex];
492     [self eq_changePreset: o_eq_presets_popup];
493
494     
495     [o_eq_preamp_sld setFloatValue:[[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:currentPresetIndex] floatValue]];
496     [self setBandSliderValuesForPreset:currentPresetIndex];
497 }
498
499 - (void)equalizerUpdated
500 {
501     float f_preamp = config_GetFloat(p_intf, "equalizer-preamp");
502     bool b_2p = (BOOL)config_GetInt(p_intf, "equalizer-2pass");
503     bool b_enabled = GetEqualizerStatus(p_intf, (char *)"equalizer");
504
505     /* Setup sliders */
506     [self updatePresetSelector];
507
508     /* Set the the checkboxes */
509     [o_eq_enable_ckb setState: b_enabled];
510     [o_eq_twopass_ckb setState: b_2p];
511 }
512
513 - (id)sliderByIndex:(int)index
514 {
515     switch(index) {
516         case 0 : return o_eq_band1_sld;
517         case 1 : return o_eq_band2_sld;
518         case 2 : return o_eq_band3_sld;
519         case 3 : return o_eq_band4_sld;
520         case 4 : return o_eq_band5_sld;
521         case 5 : return o_eq_band6_sld;
522         case 6 : return o_eq_band7_sld;
523         case 7 : return o_eq_band8_sld;
524         case 8 : return o_eq_band9_sld;
525         case 9 : return o_eq_band10_sld;
526         default : return nil;
527     }
528 }
529
530 - (void)setBandSliderValuesForPreset:(NSInteger)presetID
531 {
532     NSString *preset = [[[NSUserDefaults standardUserDefaults] objectForKey:@"EQValues"] objectAtIndex:presetID];
533     NSArray *values = [preset componentsSeparatedByString:@" "];
534     NSUInteger count = [values count];
535     for (NSUInteger x = 0; x < count; x++)
536         [self setValue:[[values objectAtIndex:x] floatValue] forSlider:x];
537 }
538
539 - (NSString *)generatePresetString
540 {
541     return [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
542             [o_eq_band1_sld floatValue],
543             [o_eq_band2_sld floatValue],
544             [o_eq_band3_sld floatValue],
545             [o_eq_band4_sld floatValue],
546             [o_eq_band5_sld floatValue],
547             [o_eq_band6_sld floatValue],
548             [o_eq_band7_sld floatValue],
549             [o_eq_band8_sld floatValue],
550             [o_eq_band9_sld floatValue],
551             [o_eq_band10_sld floatValue]];
552 }
553
554 - (void)setValue:(float)value forSlider:(int)index
555 {
556     id slider = [self sliderByIndex:index];
557
558     if (slider != nil)
559         [slider setFloatValue:value];
560 }
561
562 - (IBAction)eq_enable:(id)sender
563 {
564     [self setAudioFilter: "equalizer" on:[sender state]];
565 }
566
567 - (IBAction)eq_bandSliderUpdated:(id)sender
568 {
569     audio_output_t *p_aout = getAout();
570     if (p_aout) {
571         var_SetString(p_aout, "equalizer-bands", [[self generatePresetString] UTF8String]);
572         vlc_object_release(p_aout);
573     }
574
575     /* save changed to config */
576     config_PutPsz(p_intf, "equalizer-bands", [[self generatePresetString] UTF8String]);
577
578 }
579
580 - (IBAction)eq_changePreset:(id)sender
581 {
582     NSInteger numberOfChosenPreset = [sender indexOfSelectedItem];
583     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
584
585     NSString *preset = [[defaults objectForKey:@"EQValues"] objectAtIndex:numberOfChosenPreset];
586     NSString *preamp = [[defaults objectForKey:@"EQPreampValues"] objectAtIndex:numberOfChosenPreset];
587
588     audio_output_t *p_aout = getAout();
589     if (p_aout) {
590         var_SetString(p_aout, "equalizer-bands", [preset UTF8String]);
591         var_SetFloat(p_aout, "equalizer-preamp", [preamp floatValue]);
592         var_SetString(p_aout, "equalizer-preset" , [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String]);
593         vlc_object_release(p_aout);
594     }
595
596     [o_eq_preamp_sld setFloatValue: [preamp floatValue]];
597     [self setBandSliderValuesForPreset:numberOfChosenPreset];
598
599     /* save changed to config */
600     config_PutPsz(p_intf, "equalizer-bands", [preset UTF8String]);
601     config_PutFloat(p_intf, "equalizer-preamp", [preamp floatValue]);
602     config_PutPsz(p_intf, "equalizer-preset", [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String]);
603
604 }
605
606 - (IBAction)eq_preampSliderUpdated:(id)sender
607 {
608     float f_preamp = [sender floatValue] ;
609
610     audio_output_t *p_aout = getAout();
611     if (p_aout) {
612         var_SetFloat(p_aout, "equalizer-preamp", f_preamp);
613         vlc_object_release(p_aout);
614     }
615     
616     /* save changed to config */
617     config_PutFloat(p_intf, "equalizer-preamp", f_preamp);
618
619 }
620 - (IBAction)eq_twopass:(id)sender
621 {
622     bool b_2p = [sender state] ? true : false;
623
624     audio_output_t *p_aout = getAout();
625     if (p_aout) {
626         var_SetBool(p_aout, "equalizer-2pass", b_2p);
627         vlc_object_release(p_aout);
628     }
629
630     /* save changed to config */
631     config_PutInt(p_intf, "equalizer-2pass", (int)b_2p);
632
633 }
634
635 - (IBAction)addPresetAction:(id)sender
636 {
637     /* show panel */
638     VLCEnterTextPanel *panel = [VLCEnterTextPanel sharedInstance];
639     [panel setTitle: _NS("Save current selection as new preset")];
640     [panel setSubTitle: _NS("Enter a name for the new preset:")];
641     [panel setCancelButtonLabel: _NS("Cancel")];
642     [panel setOKButtonLabel: _NS("Save")];
643     [panel setTarget:self];
644     b_genericAudioProfileInInteraction = NO;
645
646     [panel runModalForWindow:o_window];
647 }
648
649 - (void)panel:(VLCEnterTextPanel *)panel returnValue:(NSUInteger)value text:(NSString *)text
650 {
651
652     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
653
654     // EQ settings
655     if (!b_genericAudioProfileInInteraction) {
656         if (value == NSOKButton && [text length] > 0) {
657             NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
658             [workArray addObject:[self generatePresetString]];
659             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
660             [workArray release];
661             workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
662             [workArray addObject:text];
663             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
664             [workArray release];
665             workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
666             [workArray addObject:[NSString stringWithFormat:@"%.1f", [o_eq_preamp_sld floatValue]]];
667             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
668             [workArray release];
669             workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
670             [workArray addObject:[text decomposedStringWithCanonicalMapping]];
671             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
672             [workArray release];
673             [defaults synchronize];
674
675             /* update VLC internals */
676             audio_output_t *p_aout = getAout();
677             if (p_aout) {
678                 var_SetString(p_aout, "equalizer-preset", [[text decomposedStringWithCanonicalMapping] UTF8String]);
679                 vlc_object_release(p_aout);
680             }
681
682             config_PutPsz(p_intf, "equalizer-preset", [[text decomposedStringWithCanonicalMapping] UTF8String]);
683
684
685             /* update UI */
686             [self updatePresetSelector];
687         }
688
689     // profile settings
690     } else {
691
692         if (value != NSOKButton) {
693             [o_profile_pop selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
694             return;
695         }
696
697         NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
698
699         // duplicate names are not allowed in the popup control
700         if ([text length] == 0 || [profileNames containsObject:text]) {
701             [o_profile_pop selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
702
703             NSAlert *alert = [[[NSAlert alloc] init] autorelease];
704             [alert setAlertStyle:NSCriticalAlertStyle];
705             [alert setMessageText:_NS("Please enter a unique name for the new profile.")];
706             [alert setInformativeText:_NS("Multiple profiles with the same name are not allowed.")];
707
708             [alert beginSheetModalForWindow:o_window
709                               modalDelegate:nil
710                              didEndSelector:nil
711                                 contextInfo:nil];
712             return;
713         }
714         
715         NSString *newProfile = [self generateProfileString];
716
717         /* add string to user defaults as well as a label */
718         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
719         NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
720         [workArray addObject:newProfile];
721         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
722         [defaults setInteger:[workArray count] - 1 forKey:@"AudioEffectSelectedProfile"];
723         [workArray release];
724         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
725         [workArray addObject:text];
726         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
727         [workArray release];
728
729         /* save defaults */
730         [defaults synchronize];
731         [self resetProfileSelector];
732     }
733 }
734
735 - (IBAction)deletePresetAction:(id)sender
736 {
737     VLCSelectItemInPopupPanel *panel = [VLCSelectItemInPopupPanel sharedInstance];
738     [panel setTitle:_NS("Remove a preset")];
739     [panel setSubTitle:_NS("Select the preset you would like to remove:")];
740     [panel setOKButtonLabel:_NS("Remove")];
741     [panel setCancelButtonLabel:_NS("Cancel")];
742     [panel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
743     [panel setTarget:self];
744     b_genericAudioProfileInInteraction = NO;
745
746     [panel runModalForWindow:o_window];
747 }
748
749 - (void)panel:(VLCSelectItemInPopupPanel *)panel returnValue:(NSUInteger)value item:(NSUInteger)item
750 {
751     if (value == NSOKButton) {
752         if (!b_genericAudioProfileInInteraction) {
753             /* remove requested profile from the arrays */
754             NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
755             NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
756             [workArray removeObjectAtIndex:item];
757             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
758             [workArray release];
759             workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
760             [workArray removeObjectAtIndex:item];
761             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
762             [workArray release];
763             workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
764             [workArray removeObjectAtIndex:item];
765             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
766             [workArray release];
767             workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
768             [workArray removeObjectAtIndex:item];
769             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
770             [workArray release];
771             [defaults synchronize];
772
773             /* update UI */
774             [self updatePresetSelector];
775         } else {
776             /* remove selected profile from settings */
777             NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
778             NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
779             [workArray removeObjectAtIndex:item];
780             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
781             [workArray release];
782             workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
783             [workArray removeObjectAtIndex:item];
784             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
785             [workArray release];
786
787             if (i_old_profile_index >= item)
788                 [defaults setInteger:i_old_profile_index - 1 forKey:@"AudioEffectSelectedProfile"];
789
790             /* save defaults */
791             [defaults synchronize];
792             [self resetProfileSelector];
793         }
794     }
795 }
796
797 #pragma mark -
798 #pragma mark Compressor
799 - (void)resetCompressor
800 {
801     char *psz_afilters;
802     psz_afilters = config_GetPsz(p_intf, "audio-filter");
803     if (psz_afilters) {
804         [o_comp_enable_ckb setState: (NSInteger)strstr(psz_afilters, "compressor") ];
805         free(psz_afilters);
806     }
807     else
808         [o_comp_enable_ckb setState: NSOffState];
809
810     [o_comp_band1_sld setFloatValue: config_GetFloat(p_intf, "compressor-rms-peak")];
811     [o_comp_band1_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [o_comp_band1_sld floatValue]]];
812     [o_comp_band2_sld setFloatValue: config_GetFloat(p_intf, "compressor-attack")];
813     [o_comp_band2_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [o_comp_band2_sld floatValue]]];
814     [o_comp_band3_sld setFloatValue: config_GetFloat(p_intf, "compressor-release")];
815     [o_comp_band3_fld setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [o_comp_band3_sld floatValue]]];
816     [o_comp_band4_sld setFloatValue: config_GetFloat(p_intf, "compressor-threshold")];
817     [o_comp_band4_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [o_comp_band4_sld floatValue]]];
818     [o_comp_band5_sld setFloatValue: config_GetFloat(p_intf, "compressor-ratio")];
819     [o_comp_band5_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [o_comp_band5_sld floatValue]]];
820     [o_comp_band6_sld setFloatValue: config_GetFloat(p_intf, "compressor-knee")];
821     [o_comp_band6_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [o_comp_band6_sld floatValue]]];
822     [o_comp_band7_sld setFloatValue: config_GetFloat(p_intf, "compressor-makeup-gain")];
823     [o_comp_band7_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [o_comp_band7_sld floatValue]]];
824 }
825
826 - (IBAction)resetCompressorValues:(id)sender
827 {
828     config_PutFloat(p_intf, "compressor-rms-peak", 0.000000);
829     config_PutFloat(p_intf, "compressor-attack", 25.000000);
830     config_PutFloat(p_intf, "compressor-release", 100.000000);
831     config_PutFloat(p_intf, "compressor-threshold", -11.000000);
832     config_PutFloat(p_intf, "compressor-ratio", 8.000000);
833     config_PutFloat(p_intf, "compressor-knee", 2.500000);
834     config_PutFloat(p_intf, "compressor-makeup-gain", 7.000000);
835
836     audio_output_t *p_aout = getAout();
837     if (p_aout) {
838         var_SetFloat(p_aout, "compressor-rms-peak", 0.000000);
839         var_SetFloat(p_aout, "compressor-attack", 25.000000);
840         var_SetFloat(p_aout, "compressor-release", 100.000000);
841         var_SetFloat(p_aout, "compressor-threshold", -11.000000);
842         var_SetFloat(p_aout, "compressor-ratio", 8.000000);
843         var_SetFloat(p_aout, "compressor-knee", 2.500000);
844         var_SetFloat(p_aout, "compressor-makeup-gain", 7.000000);
845         vlc_object_release(p_aout);
846     }
847     [self resetCompressor];
848 }
849
850 - (IBAction)comp_enable:(id)sender
851 {
852     [self setAudioFilter:"compressor" on:[sender state]];
853 }
854
855 - (IBAction)comp_sliderUpdated:(id)sender
856 {
857     audio_output_t *p_aout = getAout();
858     char *value;
859     if (sender == o_comp_band1_sld)
860         value = "compressor-rms-peak";
861     else if (sender == o_comp_band2_sld)
862         value = "compressor-attack";
863     else if (sender == o_comp_band3_sld)
864         value = "compressor-release";
865     else if (sender == o_comp_band4_sld)
866         value = "compressor-threshold";
867     else if (sender == o_comp_band5_sld)
868         value = "compressor-ratio";
869     else if (sender == o_comp_band6_sld)
870         value = "compressor-knee";
871     else if (sender == o_comp_band7_sld)
872         value = "compressor-makeup-gain";
873
874     if (p_aout) {
875         var_SetFloat(p_aout, value, [sender floatValue]);
876         vlc_object_release(p_aout);
877     }
878     config_PutFloat(p_intf, value, [sender floatValue]);
879
880     if (sender == o_comp_band1_sld)
881         [o_comp_band1_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
882     else if (sender == o_comp_band2_sld)
883         [o_comp_band2_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [sender floatValue]]];
884     else if (sender == o_comp_band3_sld)
885         [o_comp_band3_fld setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [sender floatValue]]];
886     else if (sender == o_comp_band4_sld)
887         [o_comp_band4_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [sender floatValue]]];
888     else if (sender == o_comp_band5_sld)
889         [o_comp_band5_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [sender floatValue]]];
890     else if (sender == o_comp_band6_sld)
891         [o_comp_band6_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [sender floatValue]]];
892     else if (sender == o_comp_band7_sld)
893         [o_comp_band7_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [sender floatValue]]];
894 }
895
896 #pragma mark -
897 #pragma mark Spatializer
898 - (void)resetSpatializer
899 {
900     char *psz_afilters;
901     psz_afilters = config_GetPsz(p_intf, "audio-filter");
902     if (psz_afilters) {
903         [o_spat_enable_ckb setState: (NSInteger)strstr(psz_afilters, "spatializer") ];
904         free(psz_afilters);
905     }
906     else
907         [o_spat_enable_ckb setState: NSOffState];
908
909 #define setSlider(bandsld, bandfld, var) \
910 [bandsld setFloatValue: config_GetFloat(p_intf, var) * 10.]; \
911 [bandfld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [bandsld floatValue]]]
912
913     setSlider(o_spat_band1_sld, o_spat_band1_fld, "spatializer-roomsize");
914     setSlider(o_spat_band2_sld, o_spat_band2_fld, "spatializer-width");
915     setSlider(o_spat_band3_sld, o_spat_band3_fld, "spatializer-wet");
916     setSlider(o_spat_band4_sld, o_spat_band4_fld, "spatializer-dry");
917     setSlider(o_spat_band5_sld, o_spat_band5_fld, "spatializer-damp");
918
919 #undef setSlider
920 }
921
922 - (IBAction)resetSpatializerValues:(id)sender
923 {
924     config_PutFloat(p_intf, "spatializer-roomsize", .85);
925     config_PutFloat(p_intf, "spatializer-width", 1.);
926     config_PutFloat(p_intf, "spatializer-wet", .4);
927     config_PutFloat(p_intf, "spatializer-dry", .5);
928     config_PutFloat(p_intf, "spatializer-damp", .5);
929
930     audio_output_t *p_aout = getAout();
931     if (p_aout) {
932         var_SetFloat(p_aout, "spatializer-roomsize", .85);
933         var_SetFloat(p_aout, "spatializer-width", 1.);
934         var_SetFloat(p_aout, "spatializer-wet", .4);
935         var_SetFloat(p_aout, "spatializer-dry", .5);
936         var_SetFloat(p_aout, "spatializer-damp", .5);
937         vlc_object_release(p_aout);
938     }
939     [self resetSpatializer];
940 }
941
942 - (IBAction)spat_enable:(id)sender
943 {
944     [self setAudioFilter:"spatializer" on:[sender state]];
945 }
946
947 - (IBAction)spat_sliderUpdated:(id)sender
948 {
949     audio_output_t *p_aout = getAout();
950     char *value;
951     if (sender == o_spat_band1_sld)
952         value = "spatializer-roomsize";
953     else if (sender == o_spat_band2_sld)
954         value = "spatializer-width";
955     else if (sender == o_spat_band3_sld)
956         value = "spatializer-wet";
957     else if (sender == o_spat_band4_sld)
958         value = "spatializer-dry";
959     else if (sender == o_spat_band5_sld)
960         value = "spatializer-damp";
961
962     if (p_aout) {
963         var_SetFloat(p_aout, value, [sender floatValue] / 10.);
964         vlc_object_release(p_aout);
965     }
966     config_PutFloat(p_intf, value, [sender floatValue] / 10.);
967
968     if (sender == o_spat_band1_sld)
969         [o_spat_band1_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
970     else if (sender == o_spat_band2_sld)
971         [o_spat_band2_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
972     else if (sender == o_spat_band3_sld)
973         [o_spat_band3_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
974     else if (sender == o_spat_band4_sld)
975         [o_spat_band4_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
976     else if (sender == o_spat_band5_sld)
977         [o_spat_band5_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
978 }
979
980 #pragma mark -
981 #pragma mark Filter
982 - (void)resetAudioFilters
983 {
984     char *psz_afilters;
985     psz_afilters = config_GetPsz(p_intf, "audio-filter");
986     if (psz_afilters) {
987         [o_filter_headPhone_ckb setState: (NSInteger)strstr(psz_afilters, "headphone") ];
988         [o_filter_normLevel_ckb setState: (NSInteger)strstr(psz_afilters, "normvol") ];
989         [o_filter_karaoke_ckb setState: (NSInteger)strstr(psz_afilters, "karaoke") ];
990         free(psz_afilters);
991     } else {
992         [o_filter_headPhone_ckb setState: NSOffState];
993         [o_filter_normLevel_ckb setState: NSOffState];
994         [o_filter_karaoke_ckb setState: NSOffState];
995     }
996     [o_filter_normLevel_sld setFloatValue: config_GetFloat(p_intf, "norm-max-level")];
997 }
998
999 - (IBAction)filter_enableHeadPhoneVirt:(id)sender
1000 {
1001     [self setAudioFilter: "headphone" on:[sender state]];
1002 }
1003
1004 - (IBAction)filter_enableVolumeNorm:(id)sender
1005 {
1006     [self setAudioFilter: "normvol" on:[sender state]];
1007 }
1008
1009 - (IBAction)filter_volNormSliderUpdated:(id)sender
1010 {
1011     audio_output_t *p_aout = getAout();
1012
1013     if (p_aout) {
1014         var_SetFloat(p_aout, "norm-max-level", [o_filter_normLevel_sld floatValue]);
1015         vlc_object_release(p_aout);
1016     }
1017
1018     config_PutFloat(p_intf, "norm-max-level", [o_filter_normLevel_sld floatValue]);
1019 }
1020
1021 - (IBAction)filter_enableKaraoke:(id)sender
1022 {
1023     [self setAudioFilter: "karaoke" on:[sender state]];
1024 }
1025
1026 @end