macosx: save default audio profile as custom when quiting
[vlc.git] / modules / gui / macosx / VLCAudioEffectsWindowController.m
blobb445961ac60e09c97ee3c18b60b10137d85517ae
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 - (NSInteger)getPresetIndexForProfile:(NSInteger)profileIndex
113     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
114     NSString *profile = [[defaults objectForKey:@"AudioEffectProfiles"] objectAtIndex:profileIndex];
115     NSString *presetName = B64DecNSStr([[profile componentsSeparatedByString:@";"] firstObject]);
116     return [[defaults objectForKey:@"EQNames"] indexOfObject:presetName];
119 - (void)loadProfile
121     intf_thread_t *p_intf = getIntf();
122     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
123     NSInteger selectedProfile = [defaults integerForKey:@"AudioEffectSelectedProfile"];
124     playlist_t *p_playlist = pl_Get(p_intf);
126     /* disable existing filters */
127     playlist_EnableAudioFilter(p_playlist, "equalizer", false);
128     playlist_EnableAudioFilter(p_playlist, "compressor", false);
129     playlist_EnableAudioFilter(p_playlist, "spatializer", false);
130     playlist_EnableAudioFilter(p_playlist, "compressor", false);
131     playlist_EnableAudioFilter(p_playlist, "headphone", false);
132     playlist_EnableAudioFilter(p_playlist, "normvol", false);
133     playlist_EnableAudioFilter(p_playlist, "karaoke", false);
135     /* fetch preset */
136     NSArray *items = [[[defaults objectForKey:@"AudioEffectProfiles"] objectAtIndex:(NSUInteger) selectedProfile] componentsSeparatedByString:@";"];
138     /* eq preset */
139     char const *psz_eq_preset = [B64DecNSStr([items firstObject]) UTF8String];
140     audio_output_t *p_aout = getAout();
141     if (p_aout)
142         var_SetString(p_aout, "equalizer-preset", psz_eq_preset);
143     var_SetString(p_playlist, "equalizer-preset", psz_eq_preset);
145     /* filter handling */
146     NSString *tempString = B64DecNSStr([items objectAtIndex:1]);
147     NSArray *tempArray;
148     NSUInteger count;
149     /* enable the new filters */
150     if ([tempString length] > 0) {
151         tempArray = [tempString componentsSeparatedByString:@":"];
152         count = [tempArray count];
153         for (NSUInteger x = 0; x < count; x++)
154             playlist_EnableAudioFilter(p_playlist, [[tempArray objectAtIndex:x] UTF8String], true);
155     }
157     NSInteger presetIndex = [self getPresetIndexForProfile:selectedProfile];
159     /* values */
160     var_SetFloat(p_playlist, "compressor-rms-peak",[[items objectAtIndex:2] floatValue]);
161     var_SetFloat(p_playlist, "compressor-attack",[[items objectAtIndex:3] floatValue]);
162     var_SetFloat(p_playlist, "compressor-release",[[items objectAtIndex:4] floatValue]);
163     var_SetFloat(p_playlist, "compressor-threshold",[[items objectAtIndex:5] floatValue]);
164     var_SetFloat(p_playlist, "compressor-ratio",[[items objectAtIndex:6] floatValue]);
165     var_SetFloat(p_playlist, "compressor-knee",[[items objectAtIndex:7] floatValue]);
166     var_SetFloat(p_playlist, "compressor-makeup-gain",[[items objectAtIndex:8] floatValue]);
167     var_SetFloat(p_playlist, "spatializer-roomsize",[[items objectAtIndex:9] floatValue]);
168     var_SetFloat(p_playlist, "spatializer-width",[[items objectAtIndex:10] floatValue]);
169     var_SetFloat(p_playlist, "spatializer-wet",[[items objectAtIndex:11] floatValue]);
170     var_SetFloat(p_playlist, "spatializer-dry",[[items objectAtIndex:12] floatValue]);
171     var_SetFloat(p_playlist, "spatializer-damp",[[items objectAtIndex:13] floatValue]);
172     var_SetFloat(p_playlist, "norm-max-level",[[items objectAtIndex:14] floatValue]);
173     var_SetBool(p_playlist, "equalizer-2pass",(BOOL)[[items objectAtIndex:15] intValue]);
174     var_SetString(p_playlist, "equalizer-bands", [[[defaults objectForKey:@"EQValues"] objectAtIndex:presetIndex] UTF8String]);
175     var_SetFloat(p_playlist, "equalizer-preamp", [[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:presetIndex] floatValue]);
176     var_SetString(p_playlist, "equalizer-preset", [[[defaults objectForKey:@"EQNames"] objectAtIndex:presetIndex] UTF8String]);
178     /* set values on-the-fly if we have an aout */
179     if (p_aout) {
180         var_SetFloat(p_aout, "compressor-rms-peak", [[items objectAtIndex:2] floatValue]);
181         var_SetFloat(p_aout, "compressor-attack", [[items objectAtIndex:3] floatValue]);
182         var_SetFloat(p_aout, "compressor-release", [[items objectAtIndex:4] floatValue]);
183         var_SetFloat(p_aout, "compressor-threshold", [[items objectAtIndex:5] floatValue]);
184         var_SetFloat(p_aout, "compressor-ratio", [[items objectAtIndex:6] floatValue]);
185         var_SetFloat(p_aout, "compressor-knee", [[items objectAtIndex:7] floatValue]);
186         var_SetFloat(p_aout, "compressor-makeup-gain", [[items objectAtIndex:8] floatValue]);
187         var_SetFloat(p_aout, "spatializer-roomsize", [[items objectAtIndex:9] floatValue]);
188         var_SetFloat(p_aout, "spatializer-width", [[items objectAtIndex:10] floatValue]);
189         var_SetFloat(p_aout, "spatializer-wet", [[items objectAtIndex:11] floatValue]);
190         var_SetFloat(p_aout, "spatializer-dry", [[items objectAtIndex:12] floatValue]);
191         var_SetFloat(p_aout, "spatializer-damp", [[items objectAtIndex:13] floatValue]);
192         var_SetFloat(p_aout, "norm-max-level", [[items objectAtIndex:14] floatValue]);
193         var_SetBool(p_aout, "equalizer-2pass", (BOOL)[[items objectAtIndex:15] intValue]);
194         var_SetString(p_aout, "equalizer-bands", [[[defaults objectForKey:@"EQValues"] objectAtIndex:presetIndex] UTF8String]);
195         var_SetFloat(p_aout, "equalizer-preamp", [[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:presetIndex] floatValue]);
196         var_SetString(p_aout, "equalizer-preset", [[[defaults objectForKey:@"EQNames"] objectAtIndex:presetIndex] UTF8String]);
197     }
199     /* update UI */
200     BOOL b_equalizerEnabled = [tempString rangeOfString:@"equalizer"].location != NSNotFound;
201     [_equalizerView enableSubviews:b_equalizerEnabled];
202     [_equalizerEnableCheckbox setState:(b_equalizerEnabled ? NSOnState : NSOffState)];
204     [_equalizerTwoPassCheckbox setState:[[items objectAtIndex:15] intValue]];
206     /* store current profile selection */
207     [defaults setInteger:selectedProfile forKey:@"AudioEffectSelectedProfile"];
208     [defaults synchronize];
210     if (p_aout)
211         vlc_object_release(p_aout);
214 - (void)windowDidLoad
216     [_applyProfileCheckbox setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"AudioEffectApplyProfileOnStartup"]];
218     /* setup the user's language */
219     /* Equalizer */
220     [_equalizerEnableCheckbox setTitle:_NS("Enable")];
221     [_equalizerTwoPassCheckbox setTitle:_NS("2 Pass")];
222     [_equalizerTwoPassCheckbox setToolTip:_NS("Filter the audio twice. This provides a more "  \
223                                               "intense effect.")];
224     [_equalizerPreampLabel setStringValue:_NS("Preamp")];
225     [_equalizerPreampLabel setToolTip:_NS("Set the global gain in dB (-20 ... 20).")];
227     /* Compressor */
228     [_compressorEnableCheckbox setTitle:_NS("Enable dynamic range compressor")];
229     [_compressorResetButton setTitle:_NS("Reset")];
230     [_compressorBand1Label setStringValue:_NS("RMS/peak")];;
231     [_compressorBand2Label setStringValue:_NS("Attack")];
232     [_compressorBand3Label setStringValue:_NS("Release")];
233     [_compressorBand4Label setStringValue:_NS("Threshold")];
234     [_compressorBand5Label setStringValue:_NS("Ratio")];
235     [_compressorBand6Label setStringValue:_NS("Knee radius")];
236     [_compressorBand7Label setStringValue:_NS("Makeup gain")];
238     /* Spatializer */
239     [_spatializerEnableCheckbox setTitle:_NS("Enable Spatializer")];
240     [_spatializerResetButton setTitle:_NS("Reset")];
241     [_spatializerBand1Label setStringValue:_NS("Size")];
242     [_spatializerBand1Label setToolTip:_NS("Defines the virtual surface of the room" \
243                                            " emulated by the filter.")];
244     [_spatializerBand2Label setStringValue:_NS("Width")];
245     [_spatializerBand2Label setToolTip:_NS("Width of the virtual room")];
246     [_spatializerBand3Label setStringValue:_NS("Wet")];
247     [_spatializerBand4Label setStringValue:_NS("Dry")];
248     [_spatializerBand5Label setStringValue:_NS("Damp")];
250     /* Filter */
251     [_filterHeadPhoneCheckbox setTitle:_NS("Headphone virtualization")];
252     [_filterHeadPhoneCheckbox setToolTip:_NS("This effect gives you the feeling that you are standing in a room " \
253                                              "with a complete 7.1 speaker set when using only a headphone, " \
254                                              "providing a more realistic sound experience. It should also be " \
255                                              "more comfortable and less tiring when listening to music for " \
256                                              "long periods of time.\nIt works with any source format from mono " \
257                                              "to 7.1.")];
258     [_filterNormLevelCheckbox setTitle:_NS("Volume normalization")];
259     [_filterNormLevelCheckbox setToolTip:_NS("Volume normalizer")];
260     [_filterNormLevelLabel setToolTip:_NS("If the average power over the last N buffers " \
261                                           "is higher than this value, the volume will be normalized. " \
262                                           "This value is a positive floating point number. A value " \
263                                           "between 0.5 and 10 seems sensible.")];
264     [_filterNormLevelLabel setStringValue:_NS("Maximum level")];
265     [_filterKaraokeCheckbox setTitle:_NS("Karaoke")];
266     [_filterKaraokeCheckbox setToolTip:_NS("Simple Karaoke filter")];
267     [_filterScaleTempoCheckbox setTitle:_NS("Scaletempo")];
268     [_filterScaleTempoCheckbox setToolTip:_NS("Audio tempo scaler synched with rate")];
269     [_filterStereoEnhancerCheckbox setTitle:_NS("Stereo Enhancer")];
270     [_filterStereoEnhancerCheckbox setToolTip:_NS("This filter enhances the stereo effect by "\
271                                                   "suppressing mono (signal common to both channels) "\
272                                                   "and by delaying the signal of left into right and vice versa, "\
273                                                   "thereby widening the stereo effect.")];
275     /* generic */
276     [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"equalizer"]] setLabel:_NS("Equalizer")];
277     [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"compressor"]] setLabel:_NS("Compressor")];
278     [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"spatializer"]] setLabel:_NS("Spatializer")];
279     [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"filter"]] setLabel:_NS("Filter")];
280     [self.window setTitle:_NS("Audio Effects")];
281     [self.window setExcludedFromWindowsMenu:YES];
282     [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
284     [self equalizerUpdated];
285     [self resetCompressor];
286     [self resetSpatializer];
287     [self resetAudioFilters];
288     [self resetProfileSelector];
291 #pragma mark -
292 #pragma mark internal functions
294 - (void)setAudioFilter: (char *)psz_name on:(BOOL)b_on
296     playlist_EnableAudioFilter(pl_Get(getIntf()), psz_name, b_on);
299 - (void)resetProfileSelector
301     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
302     [_profilePopup removeAllItems];
304     NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
305     [_profilePopup addItemsWithTitles:profileNames];
307     [[_profilePopup menu] addItem:[NSMenuItem separatorItem]];
308     [_profilePopup addItemWithTitle:_NS("Duplicate current profile...")];
309     [[_profilePopup lastItem] setTarget: self];
310     [[_profilePopup lastItem] setAction: @selector(addAudioEffectsProfile:)];
312     if ([profileNames count] > 1) {
313         [_profilePopup addItemWithTitle:_NS("Organize Profiles...")];
314         [[_profilePopup lastItem] setTarget: self];
315         [[_profilePopup lastItem] setAction: @selector(removeAudioEffectsProfile:)];
316     }
318     [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
319     if (i_old_profile_index || [[NSUserDefaults standardUserDefaults] integerForKey:@"AudioEffectSelectedProfile"])
320         [self profileSelectorAction:self];
323 #pragma mark -
324 #pragma mark generic code
325 - (void)updateCocoaWindowLevel:(NSInteger)i_level
327     if (self.isWindowLoaded && [self.window isVisible] && [self.window level] != i_level)
328         [self.window setLevel: i_level];
331 - (IBAction)toggleWindow:(id)sender
333     if ([self.window isKeyWindow])
334         [self.window orderOut:sender];
335     else {
336         [self.window setLevel: [[[VLCMain sharedInstance] voutController] currentStatusWindowLevel]];
337         [self.window makeKeyAndOrderFront:sender];
338     }
341 - (NSString *)generateProfileString
343     playlist_t *p_playlist = pl_Get(getIntf());
344     return [NSString stringWithFormat:@"%@;%@;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i",
345                      B64EncAndFree(var_GetNonEmptyString(p_playlist, "equalizer-preset")),
346                      B64EncAndFree(var_InheritString(p_playlist, "audio-filter")),
347                      var_InheritFloat(p_playlist, "compressor-rms-peak"),
348                      var_InheritFloat(p_playlist, "compressor-attack"),
349                      var_InheritFloat(p_playlist, "compressor-release"),
350                      var_InheritFloat(p_playlist, "compressor-threshold"),
351                      var_InheritFloat(p_playlist, "compressor-ratio"),
352                      var_InheritFloat(p_playlist, "compressor-knee"),
353                      var_InheritFloat(p_playlist, "compressor-makeup-gain"),
354                      var_InheritFloat(p_playlist, "spatializer-roomsize"),
355                      var_InheritFloat(p_playlist, "spatializer-width"),
356                      var_InheritFloat(p_playlist, "spatializer-wet"),
357                      var_InheritFloat(p_playlist, "spatializer-dry"),
358                      var_InheritFloat(p_playlist, "spatializer-damp"),
359                      var_InheritFloat(p_playlist, "norm-max-level"),
360                      var_InheritBool(p_playlist,"equalizer-2pass")];
363 - (void)saveCurrentProfile
365     if (!i_old_profile_index || i_old_profile_index == -1)
366         return;
368     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
369     /* fetch all the current settings in a uniform string */
370     NSString *newProfile = [self generateProfileString];
372     NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
373     if (i_old_profile_index >= [workArray count])
374         return;
376     [workArray replaceObjectAtIndex:i_old_profile_index withObject:newProfile];
377     [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
378     [defaults synchronize];
381 - (void)saveCurrentProfileAtTerminate
383     if (i_old_profile_index)
384         return [self saveCurrentProfile];
386     playlist_t *p_playlist = pl_Get(getIntf());
387     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
388     NSString *newPresetString = [NSString stringWithCString:var_InheritString(p_playlist, "equalizer-bands") encoding:NSASCIIStringEncoding];
389     float newPresetPreamp = var_InheritFloat(p_playlist, "equalizer-preamp");
391     NSInteger defaultPresetIndex = [self getPresetIndexForProfile:0];
392     NSString *defaultPresetString = [[defaults objectForKey:@"EQValues"] objectAtIndex:defaultPresetIndex];
393     float defaultPresetPreamp = [[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:defaultPresetIndex] floatValue];
394     if ([[self generateProfileString] compare:[[defaults objectForKey:@"AudioEffectProfiles"] firstObject]] == NSOrderedSame &&
395         [newPresetString compare:defaultPresetString] == NSOrderedSame &&
396         newPresetPreamp == defaultPresetPreamp)
397         return;
399     NSMutableArray *workArray;
400     int num_custom;
402     if ([newPresetString compare:defaultPresetString] != NSOrderedSame ||
403         newPresetPreamp != defaultPresetPreamp)
404     {
405         /* preset title */
406         NSArray<NSString *> *presetTitles = [defaults objectForKey:@"EQTitles"];
407         NSString *newPresetTitle;
409         num_custom = 0;
410         do
411             newPresetTitle = [@"Custom" stringByAppendingString:[NSString stringWithFormat:@"%03i",num_custom++]];
412         while ([presetTitles containsObject:newPresetTitle]);
414         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
415         [workArray addObject:newPresetTitle];
416         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
418         /* preset name */
419         NSString *decomposedStringWithCanonicalMapping = [newPresetTitle decomposedStringWithCanonicalMapping];
420         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
421         [workArray addObject:decomposedStringWithCanonicalMapping];
422         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
423         var_SetString(p_playlist, "equalizer-preset", [decomposedStringWithCanonicalMapping UTF8String]);
425         /* preset bands */
426         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
427         [workArray addObject:newPresetString];
428         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
430         /* preset preamp */
431         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
432         [workArray addObject:[NSString stringWithFormat:@"%.1f", [_equalizerPreampSlider floatValue]]];
433         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
434     }
436     /* profile string */
437     workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
438     [workArray addObject:[self generateProfileString]];
439     [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
441     /* profile name */
442     NSArray<NSString *> *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
443     NSString *newProfileName;
445     num_custom = 0;
446     do
447         newProfileName = [@"Custom" stringByAppendingString:[NSString stringWithFormat:@"%03i",num_custom++]];
448     while ([profileNames containsObject:newProfileName]);
450     workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
451     [workArray addObject:newProfileName];
452     [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
454     [defaults synchronize];
457 - (IBAction)profileSelectorAction:(id)sender
459     [self saveCurrentProfile];
460     i_old_profile_index = [_profilePopup indexOfSelectedItem];
461     [[NSUserDefaults standardUserDefaults] setInteger:i_old_profile_index forKey:@"AudioEffectSelectedProfile"];
462     [self loadProfile];
464     /* update UI */
465     [self resetCompressor];
466     [self resetSpatializer];
467     [self resetAudioFilters];
468     [self updatePresetSelector];
471 - (void)addAudioEffectsProfile:(id)sender
473     /* show panel */
474     [_textfieldPanel setTitleString:_NS("Duplicate current profile for a new profile")];
475     [_textfieldPanel setSubTitleString:_NS("Enter a name for the new profile:")];
476     [_textfieldPanel setCancelButtonString:_NS("Cancel")];
477     [_textfieldPanel setOkButtonString:_NS("Save")];
479     __weak typeof(self) _self = self;
480     [_textfieldPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSString *resultingText) {
482         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
483         if (returnCode != NSOKButton) {
484             [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
485             return;
486         }
488         NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
490         // duplicate names are not allowed in the popup control
491         if ([resultingText length] == 0 || [profileNames containsObject:resultingText]) {
492             [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
494             NSAlert *alert = [[NSAlert alloc] init];
495             [alert setAlertStyle:NSCriticalAlertStyle];
496             [alert setMessageText:_NS("Please enter a unique name for the new profile.")];
497             [alert setInformativeText:_NS("Multiple profiles with the same name are not allowed.")];
499             [alert beginSheetModalForWindow:_self.window
500                               modalDelegate:nil
501                              didEndSelector:nil
502                                 contextInfo:nil];
503             return;
504         }
506         NSString *newProfile = [_self generateProfileString];
508         /* add string to user defaults as well as a label */
509         NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
510         [workArray addObject:newProfile];
511         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
512         [defaults setInteger:[workArray count] - 1 forKey:@"AudioEffectSelectedProfile"];
513         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
514         [workArray addObject:resultingText];
515         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
517         /* save defaults */
518         [defaults synchronize];
519         [_self resetProfileSelector];
521     }];
524 - (void)removeAudioEffectsProfile:(id)sender
526     /* show panel */
527     [_popupPanel setTitleString:_NS("Remove a preset")];
528     [_popupPanel setSubTitleString:_NS("Select the preset you would like to remove:")];
529     [_popupPanel setOkButtonString:_NS("Remove")];
530     [_popupPanel setCancelButtonString:_NS("Cancel")];
531     [_popupPanel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"AudioEffectProfileNames"]];
533     __weak typeof(self) _self = self;
534     [_popupPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSInteger selectedIndex) {
536         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
537         if (returnCode != NSOKButton) {
538             [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
539             return;
540         }
542         if (!selectedIndex) { // TODO: add popup to notify user
543             [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
544             return;
545         }
547         /* remove selected profile from settings */
548         NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
549         [workArray removeObjectAtIndex:selectedIndex];
550         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
551         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
552         [workArray removeObjectAtIndex:selectedIndex];
553         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
555         if (i_old_profile_index >= selectedIndex)
556             [defaults setInteger:i_old_profile_index - 1 forKey:@"AudioEffectSelectedProfile"];
558         /* save defaults */
559         [defaults synchronize];
560         [_self resetProfileSelector];
561     }];
564 - (IBAction)applyProfileCheckboxChanged:(id)sender
566     [[NSUserDefaults standardUserDefaults] setBool:[sender state] forKey:@"AudioEffectApplyProfileOnStartup"];
569 #pragma mark -
570 #pragma mark Equalizer
571 static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
572                                char *psz_name)
574     char *psz_parser, *psz_string = NULL;
575     audio_output_t *p_aout = getAout();
576     if (!p_aout)
577         return false;
579     psz_string = var_InheritString(pl_Get(p_custom_intf), "audio-filter");
581     if (!psz_string)
582         psz_string = var_GetNonEmptyString(p_aout, "audio-filter");
584     vlc_object_release(p_aout);
586     if (!psz_string)
587         return false;
589     psz_parser = strstr(psz_string, psz_name);
591     free(psz_string);
593     if (psz_parser)
594         return true;
595     else
596         return false;
599 - (void)updatePresetSelector
601     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
602     NSArray *presets = [defaults objectForKey:@"EQNames"];
604     [_equalizerPresetsPopup removeAllItems];
605     [_equalizerPresetsPopup addItemsWithTitles:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
606     [[_equalizerPresetsPopup menu] addItem:[NSMenuItem separatorItem]];
607     [_equalizerPresetsPopup addItemWithTitle:_NS("Add new Preset...")];
608     [[_equalizerPresetsPopup lastItem] setTarget: self];
609     [[_equalizerPresetsPopup lastItem] setAction: @selector(addPresetAction:)];
611     if ([presets count] > 1) {
612         [_equalizerPresetsPopup addItemWithTitle:_NS("Organize Presets...")];
613         [[_equalizerPresetsPopup lastItem] setTarget: self];
614         [[_equalizerPresetsPopup lastItem] setAction: @selector(deletePresetAction:)];
615     }
617     audio_output_t *p_aout = getAout();
619     NSString *currentPreset = nil;
620     if (p_aout) {
621         char *psz_preset_string = var_GetNonEmptyString(p_aout, "equalizer-preset");
622         currentPreset = toNSStr(psz_preset_string);
623         free(psz_preset_string);
624         vlc_object_release(p_aout);
625     }
627     NSUInteger currentPresetIndex = 0;
628     if (currentPreset && [currentPreset length] > 0) {
629         currentPresetIndex = [presets indexOfObject:currentPreset];
631         if (currentPresetIndex == NSNotFound)
632             currentPresetIndex = [presets count] - 1;
633     }
635     [_equalizerPresetsPopup selectItemAtIndex:currentPresetIndex];
636     [self equalizerChangePreset:_equalizerPresetsPopup];
638     [_equalizerPreampSlider setFloatValue:[[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:currentPresetIndex] floatValue]];
639     [self setBandSliderValuesForPreset:currentPresetIndex];
642 - (void)equalizerUpdated
644     intf_thread_t *p_intf = getIntf();
645     playlist_t *p_playlist = pl_Get(p_intf);
646     bool b_2p = var_CreateGetBool(p_playlist, "equalizer-2pass");
647     bool bEnabled = GetEqualizerStatus(p_intf, (char *)"equalizer");
649     /* Setup sliders */
650     var_Create(p_playlist, "equalizer-preset",
651                VLC_VAR_STRING | VLC_VAR_DOINHERIT);
652     var_Create(p_playlist, "equalizer-bands",
653                VLC_VAR_STRING | VLC_VAR_DOINHERIT);
654     var_Create(p_playlist, "equalizer-preamp",
655                VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
656     [self updatePresetSelector];
658     /* Set the the checkboxes */
659     [_equalizerView enableSubviews: bEnabled];
660     [_equalizerEnableCheckbox setState: bEnabled];
661     [_equalizerTwoPassCheckbox setState: b_2p];
664 - (id)sliderByIndex:(int)index
666     switch(index) {
667         case 0 : return _equalizerBand1Slider;
668         case 1 : return _equalizerBand2Slider;
669         case 2 : return _equalizerBand3Slider;
670         case 3 : return _equalizerBand4Slider;
671         case 4 : return _equalizerBand5Slider;
672         case 5 : return _equalizerBand6Slider;
673         case 6 : return _equalizerBand7Slider;
674         case 7 : return _equalizerBand8Slider;
675         case 8 : return _equalizerBand9Slider;
676         case 9 : return _equalizerBand10Slider;
677         default : return nil;
678     }
681 - (void)setBandSliderValuesForPreset:(NSInteger)presetID
683     NSString *preset = [[[NSUserDefaults standardUserDefaults] objectForKey:@"EQValues"] objectAtIndex:presetID];
684     NSArray *values = [preset componentsSeparatedByString:@" "];
685     NSUInteger count = [values count];
686     for (NSUInteger x = 0; x < count; x++)
687         [self setValue:[[values objectAtIndex:x] floatValue] forSlider:x];
690 - (NSString *)generatePresetString
692     return [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
693             [_equalizerBand1Slider floatValue],
694             [_equalizerBand2Slider floatValue],
695             [_equalizerBand3Slider floatValue],
696             [_equalizerBand4Slider floatValue],
697             [_equalizerBand5Slider floatValue],
698             [_equalizerBand6Slider floatValue],
699             [_equalizerBand7Slider floatValue],
700             [_equalizerBand8Slider floatValue],
701             [_equalizerBand9Slider floatValue],
702             [_equalizerBand10Slider floatValue]];
705 - (void)setValue:(float)value forSlider:(int)index
707     id slider = [self sliderByIndex:index];
709     if (slider != nil)
710         [slider setFloatValue:value];
713 - (IBAction)equalizerEnable:(id)sender
715     [_equalizerView enableSubviews:[sender state]];
716     [self setAudioFilter: "equalizer" on:[sender state]];
719 - (IBAction)equalizerBandSliderUpdated:(id)sender
721     audio_output_t *p_aout = getAout();
722     char const *psz_preset_values = [[self generatePresetString] UTF8String];
723     if (p_aout) {
724         var_SetString(p_aout, "equalizer-bands", psz_preset_values);
725         vlc_object_release(p_aout);
726     }
727     var_SetString(pl_Get(getIntf()), "equalizer-bands", psz_preset_values);
730 - (IBAction)equalizerChangePreset:(id)sender
732     NSInteger numberOfChosenPreset = [sender indexOfSelectedItem];
733     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
735     char const *psz_eq_bands = [[[defaults objectForKey:@"EQValues"] objectAtIndex:numberOfChosenPreset] UTF8String];
736     float f_eq_preamp = [[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:numberOfChosenPreset] floatValue];
737     char const *psz_eq_preset = [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String];
739     audio_output_t *p_aout = getAout();
740     if (p_aout) {
741         var_SetString(p_aout, "equalizer-bands", psz_eq_bands);
742         var_SetFloat(p_aout, "equalizer-preamp", f_eq_preamp);
743         var_SetString(p_aout, "equalizer-preset" , psz_eq_preset);
744         vlc_object_release(p_aout);
745     }
747     [_equalizerPreampSlider setFloatValue: f_eq_preamp];
748     [self setBandSliderValuesForPreset:numberOfChosenPreset];
750     var_SetString(pl_Get(getIntf()), "equalizer-bands", psz_eq_bands);
751     var_SetFloat(pl_Get(getIntf()), "equalizer-preamp", f_eq_preamp);
752     var_SetString(pl_Get(getIntf()), "equalizer-preset", psz_eq_preset);
755 - (IBAction)equalizerPreAmpSliderUpdated:(id)sender
757     float fPreamp = [sender floatValue] ;
759     audio_output_t *p_aout = getAout();
760     if (p_aout) {
761         var_SetFloat(p_aout, "equalizer-preamp", fPreamp);
762         vlc_object_release(p_aout);
763     }
764     var_SetFloat(pl_Get(getIntf()), "equalizer-preamp", fPreamp);
767 - (IBAction)equalizerTwoPass:(id)sender
769     bool b_2p = [sender state] ? true : false;
771     audio_output_t *p_aout = getAout();
772     if (p_aout) {
773         var_SetBool(p_aout, "equalizer-2pass", b_2p);
774         vlc_object_release(p_aout);
775     }
777     var_SetBool(pl_Get(getIntf()), "equalizer-2pass", b_2p);
780 - (IBAction)addPresetAction:(id)sender
782     /* show panel */
783     [_textfieldPanel setTitleString:_NS("Save current selection as new preset")];
784     [_textfieldPanel setSubTitleString:_NS("Enter a name for the new preset:")];
785     [_textfieldPanel setCancelButtonString:_NS("Cancel")];
786     [_textfieldPanel setOkButtonString:_NS("Save")];
788     __weak typeof(self) _self = self;
789     [_textfieldPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSString *resultingText) {
790         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
792         // EQ settings
793         if (returnCode != NSOKButton || [resultingText length] == 0)
794             return;
796         NSString *decomposedStringWithCanonicalMapping = [resultingText decomposedStringWithCanonicalMapping];
797         NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
798         [workArray addObject:[self generatePresetString]];
799         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
800         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
801         [workArray addObject:resultingText];
802         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
803         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
804         [workArray addObject:[NSString stringWithFormat:@"%.1f", [_equalizerPreampSlider floatValue]]];
805         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
806         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
807         [workArray addObject:decomposedStringWithCanonicalMapping];
808         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
809         [defaults synchronize];
811         /* update VLC internals */
812         char const *psz_eq_preset = [decomposedStringWithCanonicalMapping UTF8String];
813         audio_output_t *p_aout = getAout();
814         if (p_aout) {
815             var_SetString(p_aout, "equalizer-preset", psz_eq_preset);
816             vlc_object_release(p_aout);
817         }
819         var_SetString(pl_Get(getIntf()), "equalizer-preset", psz_eq_preset);
821         /* update UI */
822         [_self updatePresetSelector];
823     }];
826 - (IBAction)deletePresetAction:(id)sender
828     [_popupPanel setTitleString:_NS("Remove a preset")];
829     [_popupPanel setSubTitleString:_NS("Select the preset you would like to remove:")];
830     [_popupPanel setOkButtonString:_NS("Remove")];
831     [_popupPanel setCancelButtonString:_NS("Cancel")];
832     [_popupPanel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
834     __weak typeof(self) _self = self;
835     [_popupPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSInteger selectedIndex) {
837         if (returnCode != NSOKButton)
838             return;
840         /* remove requested profile from the arrays */
841         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
842         NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
843         [workArray removeObjectAtIndex:selectedIndex];
844         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
845         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
846         [workArray removeObjectAtIndex:selectedIndex];
847         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
848         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
849         [workArray removeObjectAtIndex:selectedIndex];
850         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
851         workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
852         [workArray removeObjectAtIndex:selectedIndex];
853         [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
854         [defaults synchronize];
856         /* update UI */
857         [_self updatePresetSelector];
858     }];
861 #pragma mark -
862 #pragma mark Compressor
863 - (void)resetCompressor
865     intf_thread_t *p_intf = getIntf();
866     playlist_t *p_playlist = pl_Get(p_intf);
867     BOOL bEnable_compressor = NO;
868     char *psz_afilters;
869     psz_afilters = var_InheritString(p_playlist, "audio-filter");
870     if (psz_afilters) {
871         bEnable_compressor = strstr(psz_afilters, "compressor") != NULL;
872         [_compressorEnableCheckbox setState: (NSInteger)strstr(psz_afilters, "compressor") ];
873         free(psz_afilters);
874     }
876     [_compressorView enableSubviews:bEnable_compressor];
877     [_compressorEnableCheckbox setState:(bEnable_compressor ? NSOnState : NSOffState)];
879     [_compressorBand1Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-rms-peak")];
880     [_compressorBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [_compressorBand1Slider floatValue]]];
881     [_compressorBand2Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-attack")];
882     [_compressorBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [_compressorBand2Slider floatValue]]];
883     [_compressorBand3Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-release")];
884     [_compressorBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [_compressorBand3Slider floatValue]]];
885     [_compressorBand4Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-threshold")];
886     [_compressorBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [_compressorBand4Slider floatValue]]];
887     [_compressorBand5Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-ratio")];
888     [_compressorBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [_compressorBand5Slider floatValue]]];
889     [_compressorBand6Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-knee")];
890     [_compressorBand6TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [_compressorBand6Slider floatValue]]];
891     [_compressorBand7Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-makeup-gain")];
892     [_compressorBand7TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [_compressorBand7Slider floatValue]]];
895 - (IBAction)resetCompressorValues:(id)sender
897     playlist_t *p_playlist = pl_Get(getIntf());
898     var_SetFloat(p_playlist, "compressor-rms-peak", 0.000000);
899     var_SetFloat(p_playlist, "compressor-attack", 25.000000);
900     var_SetFloat(p_playlist, "compressor-release", 100.000000);
901     var_SetFloat(p_playlist, "compressor-threshold", -11.000000);
902     var_SetFloat(p_playlist, "compressor-ratio", 8.000000);
903     var_SetFloat(p_playlist, "compressor-knee", 2.500000);
904     var_SetFloat(p_playlist, "compressor-makeup-gain", 7.000000);
906     audio_output_t *p_aout = getAout();
907     if (p_aout) {
908         var_SetFloat(p_aout, "compressor-rms-peak", 0.000000);
909         var_SetFloat(p_aout, "compressor-attack", 25.000000);
910         var_SetFloat(p_aout, "compressor-release", 100.000000);
911         var_SetFloat(p_aout, "compressor-threshold", -11.000000);
912         var_SetFloat(p_aout, "compressor-ratio", 8.000000);
913         var_SetFloat(p_aout, "compressor-knee", 2.500000);
914         var_SetFloat(p_aout, "compressor-makeup-gain", 7.000000);
915         vlc_object_release(p_aout);
916     }
917     [self resetCompressor];
920 - (IBAction)compressorEnable:(id)sender
922     [_compressorView enableSubviews:[sender state]];
923     [self setAudioFilter:"compressor" on:[sender state]];
926 - (IBAction)compressorSliderUpdated:(id)sender
928     audio_output_t *p_aout = getAout();
929     char *psz_property;
930     float f_value = [sender floatValue];
932     if (sender == _compressorBand1Slider)
933         psz_property = "compressor-rms-peak";
934     else if (sender == _compressorBand2Slider)
935         psz_property = "compressor-attack";
936     else if (sender == _compressorBand3Slider)
937         psz_property = "compressor-release";
938     else if (sender == _compressorBand4Slider)
939         psz_property = "compressor-threshold";
940     else if (sender == _compressorBand5Slider)
941         psz_property = "compressor-ratio";
942     else if (sender == _compressorBand6Slider)
943         psz_property = "compressor-knee";
944     else if (sender == _compressorBand7Slider)
945         psz_property = "compressor-makeup-gain";
947     if (p_aout) {
948         var_SetFloat(p_aout, psz_property, f_value);
949         vlc_object_release(p_aout);
950     }
951     var_SetFloat(pl_Get(getIntf()), psz_property, f_value);
953     if (sender == _compressorBand1Slider)
954         [_compressorBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
955     else if (sender == _compressorBand2Slider)
956         [_compressorBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", f_value]];
957     else if (sender == _compressorBand3Slider)
958         [_compressorBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", f_value]];
959     else if (sender == _compressorBand4Slider)
960         [_compressorBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", f_value]];
961     else if (sender == _compressorBand5Slider)
962         [_compressorBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", f_value]];
963     else if (sender == _compressorBand6Slider)
964         [_compressorBand6TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", f_value]];
965     else if (sender == _compressorBand7Slider)
966         [_compressorBand7TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", f_value]];
969 #pragma mark -
970 #pragma mark Spatializer
971 - (void)resetSpatializer
973     playlist_t *p_playlist = pl_Get(getIntf());
974     BOOL bEnable_spatializer = NO;
975     char *psz_afilters;
976     psz_afilters = var_InheritString(p_playlist, "audio-filter");
977     if (psz_afilters) {
978         bEnable_spatializer = strstr(psz_afilters, "spatializer") != NULL;
979         free(psz_afilters);
980     }
982     [_spatializerView enableSubviews:bEnable_spatializer];
983     [_spatializerEnableCheckbox setState:(bEnable_spatializer ? NSOnState : NSOffState)];
986 #define setSlider(bandsld, bandfld, var) \
987 [bandsld setFloatValue: var_CreateGetFloat(p_playlist, var) * 10.]; \
988 [bandfld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [bandsld floatValue]]]
990     setSlider(_spatializerBand1Slider, _spatializerBand1TextField, "spatializer-roomsize");
991     setSlider(_spatializerBand2Slider, _spatializerBand2TextField, "spatializer-width");
992     setSlider(_spatializerBand3Slider, _spatializerBand3TextField, "spatializer-wet");
993     setSlider(_spatializerBand4Slider, _spatializerBand4TextField, "spatializer-dry");
994     setSlider(_spatializerBand5Slider, _spatializerBand5TextField, "spatializer-damp");
996 #undef setSlider
999 - (IBAction)resetSpatializerValues:(id)sender
1001     playlist_t *p_playlist = pl_Get(getIntf());
1002     var_SetFloat(p_playlist, "spatializer-roomsize", .85);
1003     var_SetFloat(p_playlist, "spatializer-width", 1.);
1004     var_SetFloat(p_playlist, "spatializer-wet", .4);
1005     var_SetFloat(p_playlist, "spatializer-dry", .5);
1006     var_SetFloat(p_playlist, "spatializer-damp", .5);
1008     audio_output_t *p_aout = getAout();
1009     if (p_aout) {
1010         var_SetFloat(p_aout, "spatializer-roomsize", .85);
1011         var_SetFloat(p_aout, "spatializer-width", 1.);
1012         var_SetFloat(p_aout, "spatializer-wet", .4);
1013         var_SetFloat(p_aout, "spatializer-dry", .5);
1014         var_SetFloat(p_aout, "spatializer-damp", .5);
1015         vlc_object_release(p_aout);
1016     }
1017     [self resetSpatializer];
1020 - (IBAction)spatializerEnable:(id)sender
1022     [_spatializerView enableSubviews:[sender state]];
1023     [self setAudioFilter:"spatializer" on:[sender state]];
1026 - (IBAction)spatializerSliderUpdated:(id)sender
1028     audio_output_t *p_aout = getAout();
1029     char *psz_property = NULL;
1030     float f_value = [sender floatValue];
1032     if (sender == _spatializerBand1Slider)
1033         psz_property = "spatializer-roomsize";
1034     else if (sender == _spatializerBand2Slider)
1035         psz_property = "spatializer-width";
1036     else if (sender == _spatializerBand3Slider)
1037         psz_property = "spatializer-wet";
1038     else if (sender == _spatializerBand4Slider)
1039         psz_property = "spatializer-dry";
1040     else if (sender == _spatializerBand5Slider)
1041         psz_property = "spatializer-damp";
1043     if (p_aout) {
1044         var_SetFloat(p_aout, psz_property, f_value / 10.f);
1045         vlc_object_release(p_aout);
1046     }
1047     var_SetFloat(pl_Get(getIntf()), psz_property, f_value / 10.f);
1049     if (sender == _spatializerBand1Slider)
1050         [_spatializerBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
1051     else if (sender == _spatializerBand2Slider)
1052         [_spatializerBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
1053     else if (sender == _spatializerBand3Slider)
1054         [_spatializerBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
1055     else if (sender == _spatializerBand4Slider)
1056         [_spatializerBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
1057     else if (sender == _spatializerBand5Slider)
1058         [_spatializerBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
1061 #pragma mark -
1062 #pragma mark Filter
1063 - (void)resetAudioFilters
1065     playlist_t *p_playlist = pl_Get(getIntf());
1066     BOOL bEnable_normvol = NO;
1067     char *psz_afilters;
1068     psz_afilters = var_InheritString(p_playlist, "audio-filter");
1069     if (psz_afilters) {
1070         [_filterHeadPhoneCheckbox setState: (NSInteger)strstr(psz_afilters, "headphone") ];
1071         [_filterKaraokeCheckbox setState: (NSInteger)strstr(psz_afilters, "karaoke") ];
1072         [_filterScaleTempoCheckbox setState: (NSInteger)strstr(psz_afilters, "scaletempo") ];
1073         [_filterStereoEnhancerCheckbox setState: (NSInteger)strstr(psz_afilters, "stereo_widen") ];
1074         bEnable_normvol = strstr(psz_afilters, "normvol") != NULL;
1075         free(psz_afilters);
1076     } else {
1077         [_filterHeadPhoneCheckbox setState: NSOffState];
1078         [_filterKaraokeCheckbox setState: NSOffState];
1079         [_filterScaleTempoCheckbox setState: NSOffState];
1080         [_filterStereoEnhancerCheckbox setState: NSOffState];
1081     }
1083     [_filterNormLevelSlider setEnabled:bEnable_normvol];
1084     [_filterNormLevelLabel setEnabled:bEnable_normvol];
1085     [_filterNormLevelCheckbox setState:(bEnable_normvol ? NSOnState : NSOffState)];
1087     [_filterNormLevelSlider setFloatValue: var_CreateGetFloat(p_playlist, "norm-max-level")];
1090 - (IBAction)filterEnableHeadPhoneVirt:(id)sender
1092     [self setAudioFilter:"headphone" on:[sender state]];
1095 - (IBAction)filterEnableVolumeNorm:(id)sender
1097     [_filterNormLevelSlider setEnabled:[sender state]];
1098     [_filterNormLevelLabel setEnabled:[sender state]];
1099     [self setAudioFilter:"normvol" on:[sender state]];
1102 - (IBAction)filterVolumeNormSliderUpdated:(id)sender
1104     audio_output_t *p_aout = getAout();
1105     float f_value = [_filterNormLevelSlider floatValue];
1107     if (p_aout) {
1108         var_SetFloat(p_aout, "norm-max-level", f_value);
1109         vlc_object_release(p_aout);
1110     }
1112     var_SetFloat(pl_Get(getIntf()), "norm-max-level", f_value);
1115 - (IBAction)filterEnableKaraoke:(id)sender
1117     [self setAudioFilter:"karaoke" on:[sender state]];
1120 - (IBAction)filterEnableScaleTempo:(id)sender
1122     [self setAudioFilter:"scaletempo" on:[sender state]];
1125 - (IBAction)filterEnableStereoEnhancer:(id)sender
1127     [self setAudioFilter:"stereo_widen" on:[sender state]];
1130 @end