1 /*****************************************************************************
2 * VLCAudioEffectsWindowController.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2004-2017 VLC authors and VideoLAN
7 * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8 * Jérôme Decoodt <djc@videolan.org>
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.
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.
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 *****************************************************************************/
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>
40 @interface VLCAudioEffectsWindowController ()
42 NSInteger i_old_profile_index;
44 - (void)resetProfileSelector;
45 - (void)updatePresetSelector;
46 - (void)setBandSliderValuesForPreset:(NSInteger)presetID;
50 #pragma mark Initialization
52 @implementation VLCAudioEffectsWindowController
56 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
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])];
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];
91 self = [super initWithWindowNibName:@"AudioEffects"];
93 i_old_profile_index = -1;
95 self.popupPanel = [[VLCPopupPanelController alloc] init];
96 self.textfieldPanel = [[VLCTextfieldPanelController alloc] init];
102 - (void)windowDidLoad
104 /* setup the user's language */
106 [_equalizerEnableCheckbox setTitle:_NS("Enable")];
107 [_equalizerTwoPassCheckbox setTitle:_NS("2 Pass")];
108 [_equalizerTwoPassCheckbox setToolTip:_NS("Filter the audio twice. This provides a more " \
110 [_equalizerPreampLabel setStringValue:_NS("Preamp")];
111 [_equalizerPreampLabel setToolTip:_NS("Set the global gain in dB (-20 ... 20).")];
114 [_compressorEnableCheckbox setTitle:_NS("Enable dynamic range compressor")];
115 [_compressorResetButton setTitle:_NS("Reset")];
116 [_compressorBand1Label setStringValue:_NS("RMS/peak")];;
117 [_compressorBand2Label setStringValue:_NS("Attack")];
118 [_compressorBand3Label setStringValue:_NS("Release")];
119 [_compressorBand4Label setStringValue:_NS("Threshold")];
120 [_compressorBand5Label setStringValue:_NS("Ratio")];
121 [_compressorBand6Label setStringValue:_NS("Knee radius")];
122 [_compressorBand7Label setStringValue:_NS("Makeup gain")];
125 [_spatializerEnableCheckbox setTitle:_NS("Enable Spatializer")];
126 [_spatializerResetButton setTitle:_NS("Reset")];
127 [_spatializerBand1Label setStringValue:_NS("Size")];
128 [_spatializerBand1Label setToolTip:_NS("Defines the virtual surface of the room" \
129 " emulated by the filter.")];
130 [_spatializerBand2Label setStringValue:_NS("Width")];
131 [_spatializerBand2Label setToolTip:_NS("Width of the virtual room")];
132 [_spatializerBand3Label setStringValue:_NS("Wet")];
133 [_spatializerBand4Label setStringValue:_NS("Dry")];
134 [_spatializerBand5Label setStringValue:_NS("Damp")];
137 [_filterHeadPhoneCheckbox setTitle:_NS("Headphone virtualization")];
138 [_filterHeadPhoneCheckbox setToolTip:_NS("This effect gives you the feeling that you are standing in a room " \
139 "with a complete 7.1 speaker set when using only a headphone, " \
140 "providing a more realistic sound experience. It should also be " \
141 "more comfortable and less tiring when listening to music for " \
142 "long periods of time.\nIt works with any source format from mono " \
144 [_filterNormLevelCheckbox setTitle:_NS("Volume normalization")];
145 [_filterNormLevelCheckbox setToolTip:_NS("Volume normalizer")];
146 [_filterNormLevelLabel setToolTip:_NS("If the average power over the last N buffers " \
147 "is higher than this value, the volume will be normalized. " \
148 "This value is a positive floating point number. A value " \
149 "between 0.5 and 10 seems sensible.")];
150 [_filterNormLevelLabel setStringValue:_NS("Maximum level")];
151 [_filterKaraokeCheckbox setTitle:_NS("Karaoke")];
152 [_filterKaraokeCheckbox setToolTip:_NS("Simple Karaoke filter")];
153 [_filterScaleTempoCheckbox setTitle:_NS("Scaletempo")];
154 [_filterScaleTempoCheckbox setToolTip:_NS("Audio tempo scaler synched with rate")];
155 [_filterStereoEnhancerCheckbox setTitle:_NS("Stereo Enhancer")];
156 [_filterStereoEnhancerCheckbox setToolTip:_NS("This filter enhances the stereo effect by "\
157 "suppressing mono (signal common to both channels) "\
158 "and by delaying the signal of left into right and vice versa, "\
159 "thereby widening the stereo effect.")];
162 [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"equalizer"]] setLabel:_NS("Equalizer")];
163 [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"compressor"]] setLabel:_NS("Compressor")];
164 [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"spatializer"]] setLabel:_NS("Spatializer")];
165 [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"filter"]] setLabel:_NS("Filter")];
166 [self.window setTitle:_NS("Audio Effects")];
167 [self.window setExcludedFromWindowsMenu:YES];
168 [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
170 [self equalizerUpdated];
171 [self resetCompressor];
172 [self resetSpatializer];
173 [self resetAudioFilters];
174 [self resetProfileSelector];
178 #pragma mark internal functions
180 - (void)setAudioFilter: (char *)psz_name on:(BOOL)b_on
182 playlist_EnableAudioFilter(pl_Get(getIntf()), psz_name, b_on);
185 - (void)resetProfileSelector
187 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
188 [_profilePopup removeAllItems];
190 NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
191 [_profilePopup addItemsWithTitles:profileNames];
193 [[_profilePopup menu] addItem:[NSMenuItem separatorItem]];
194 [_profilePopup addItemWithTitle:_NS("Duplicate current profile...")];
195 [[_profilePopup lastItem] setTarget: self];
196 [[_profilePopup lastItem] setAction: @selector(addAudioEffectsProfile:)];
198 if ([profileNames count] > 1) {
199 [_profilePopup addItemWithTitle:_NS("Organize Profiles...")];
200 [[_profilePopup lastItem] setTarget: self];
201 [[_profilePopup lastItem] setAction: @selector(removeAudioEffectsProfile:)];
204 [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
205 [self profileSelectorAction:self];
209 #pragma mark generic code
210 - (void)updateCocoaWindowLevel:(NSInteger)i_level
212 if (self.isWindowLoaded && [self.window isVisible] && [self.window level] != i_level)
213 [self.window setLevel: i_level];
216 - (IBAction)toggleWindow:(id)sender
218 if ([self.window isKeyWindow])
219 [self.window orderOut:sender];
221 [self.window setLevel: [[[VLCMain sharedInstance] voutController] currentStatusWindowLevel]];
222 [self.window makeKeyAndOrderFront:sender];
226 - (NSString *)generateProfileString
228 intf_thread_t *p_intf = getIntf();
229 vlc_object_t *p_object = VLC_OBJECT(getAout());
230 if (p_object == NULL)
231 p_object = vlc_object_hold(pl_Get(p_intf));
233 NSString *o_str = [NSString stringWithFormat:@"%@;%@;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%lli",
234 B64EncAndFree(var_GetNonEmptyString(p_object, "equalizer-preset")),
235 B64EncAndFree(config_GetPsz(p_intf, "audio-filter")),
236 config_GetFloat(p_intf, "compressor-rms-peak"),
237 config_GetFloat(p_intf, "compressor-attack"),
238 config_GetFloat(p_intf, "compressor-release"),
239 config_GetFloat(p_intf, "compressor-threshold"),
240 config_GetFloat(p_intf, "compressor-ratio"),
241 config_GetFloat(p_intf, "compressor-knee"),
242 config_GetFloat(p_intf, "compressor-makeup-gain"),
243 config_GetFloat(p_intf, "spatializer-roomsize"),
244 config_GetFloat(p_intf, "spatializer-width"),
245 config_GetFloat(p_intf, "spatializer-wet"),
246 config_GetFloat(p_intf, "spatializer-dry"),
247 config_GetFloat(p_intf, "spatializer-damp"),
248 config_GetFloat(p_intf, "norm-max-level"),
249 config_GetInt(p_intf,"equalizer-2pass")];
251 vlc_object_release(p_object);
255 - (void)saveCurrentProfile
257 if (i_old_profile_index == -1)
260 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
261 /* fetch all the current settings in a uniform string */
262 NSString *newProfile = [self generateProfileString];
264 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
265 if (i_old_profile_index >= [workArray count])
268 [workArray replaceObjectAtIndex:i_old_profile_index withObject:newProfile];
269 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
270 [defaults synchronize];
273 - (IBAction)profileSelectorAction:(id)sender
275 intf_thread_t *p_intf = getIntf();
276 [self saveCurrentProfile];
277 i_old_profile_index = [_profilePopup indexOfSelectedItem];
279 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
280 NSInteger selectedProfile = [_profilePopup indexOfSelectedItem];
281 if (selectedProfile < 0)
284 audio_output_t *p_aout = getAout();
285 playlist_t *p_playlist = pl_Get(p_intf);
288 /* disable existing filters */
289 playlist_EnableAudioFilter(p_playlist, "equalizer", false);
290 playlist_EnableAudioFilter(p_playlist, "compressor", false);
291 playlist_EnableAudioFilter(p_playlist, "spatializer", false);
292 playlist_EnableAudioFilter(p_playlist, "compressor", false);
293 playlist_EnableAudioFilter(p_playlist, "headphone", false);
294 playlist_EnableAudioFilter(p_playlist, "normvol", false);
295 playlist_EnableAudioFilter(p_playlist, "karaoke", false);
299 NSArray *items = [[[defaults objectForKey:@"AudioEffectProfiles"] objectAtIndex:(NSUInteger) selectedProfile] componentsSeparatedByString:@";"];
302 vlc_object_t *p_object = VLC_OBJECT(getAout());
303 if (p_object == NULL)
304 p_object = vlc_object_hold(pl_Get(p_intf));
305 var_SetString(p_object, "equalizer-preset", [B64DecNSStr([items firstObject]) UTF8String]);
306 vlc_object_release(p_object);
308 /* filter handling */
309 NSString *tempString = B64DecNSStr([items objectAtIndex:1]);
312 /* enable the new filters, if we have an aout */
314 if ([tempString length] > 0) {
315 tempArray = [tempString componentsSeparatedByString:@":"];
316 count = [tempArray count];
317 for (NSUInteger x = 0; x < count; x++)
318 playlist_EnableAudioFilter(p_playlist, [[tempArray objectAtIndex:x] UTF8String], true);
321 config_PutPsz(p_intf,"audio-filter",[tempString UTF8String]);
324 config_PutFloat(p_intf, "compressor-rms-peak",[[items objectAtIndex:2] floatValue]);
325 config_PutFloat(p_intf, "compressor-attack",[[items objectAtIndex:3] floatValue]);
326 config_PutFloat(p_intf, "compressor-release",[[items objectAtIndex:4] floatValue]);
327 config_PutFloat(p_intf, "compressor-threshold",[[items objectAtIndex:5] floatValue]);
328 config_PutFloat(p_intf, "compressor-ratio",[[items objectAtIndex:6] floatValue]);
329 config_PutFloat(p_intf, "compressor-knee",[[items objectAtIndex:7] floatValue]);
330 config_PutFloat(p_intf, "compressor-makeup-gain",[[items objectAtIndex:8] floatValue]);
331 config_PutFloat(p_intf, "spatializer-roomsize",[[items objectAtIndex:9] floatValue]);
332 config_PutFloat(p_intf, "spatializer-width",[[items objectAtIndex:10] floatValue]);
333 config_PutFloat(p_intf, "spatializer-wet",[[items objectAtIndex:11] floatValue]);
334 config_PutFloat(p_intf, "spatializer-dry",[[items objectAtIndex:12] floatValue]);
335 config_PutFloat(p_intf, "spatializer-damp",[[items objectAtIndex:13] floatValue]);
336 config_PutFloat(p_intf, "norm-max-level",[[items objectAtIndex:14] floatValue]);
337 config_PutInt(p_intf, "equalizer-2pass",[[items objectAtIndex:15] intValue]);
339 /* set values on-the-fly if we have an aout */
341 var_SetFloat(p_aout, "compressor-rms-peak", [[items objectAtIndex:2] floatValue]);
342 var_SetFloat(p_aout, "compressor-attack", [[items objectAtIndex:3] floatValue]);
343 var_SetFloat(p_aout, "compressor-release", [[items objectAtIndex:4] floatValue]);
344 var_SetFloat(p_aout, "compressor-threshold", [[items objectAtIndex:5] floatValue]);
345 var_SetFloat(p_aout, "compressor-ratio", [[items objectAtIndex:6] floatValue]);
346 var_SetFloat(p_aout, "compressor-knee", [[items objectAtIndex:7] floatValue]);
347 var_SetFloat(p_aout, "compressor-makeup-gain", [[items objectAtIndex:8] floatValue]);
348 var_SetFloat(p_aout, "spatializer-roomsize", [[items objectAtIndex:9] floatValue]);
349 var_SetFloat(p_aout, "spatializer-width", [[items objectAtIndex:10] floatValue]);
350 var_SetFloat(p_aout, "spatializer-wet", [[items objectAtIndex:11] floatValue]);
351 var_SetFloat(p_aout, "spatializer-dry", [[items objectAtIndex:12] floatValue]);
352 var_SetFloat(p_aout, "spatializer-damp", [[items objectAtIndex:13] floatValue]);
353 var_SetFloat(p_aout, "norm-max-level", [[items objectAtIndex:14] floatValue]);
354 var_SetBool(p_aout, "equalizer-2pass", (BOOL)[[items objectAtIndex:15] intValue]);
358 BOOL b_equalizerEnabled = [tempString rangeOfString:@"equalizer"].location != NSNotFound;
359 [_equalizerView enableSubviews:b_equalizerEnabled];
360 [_equalizerEnableCheckbox setState:(b_equalizerEnabled ? NSOnState : NSOffState)];
362 [_equalizerTwoPassCheckbox setState:[[items objectAtIndex:15] intValue]];
363 [self resetCompressor];
364 [self resetSpatializer];
365 [self resetAudioFilters];
366 [self updatePresetSelector];
368 /* store current profile selection */
369 [defaults setInteger:selectedProfile forKey:@"AudioEffectSelectedProfile"];
370 [defaults synchronize];
373 vlc_object_release(p_aout);
376 - (void)addAudioEffectsProfile:(id)sender
379 [_textfieldPanel setTitleString:_NS("Duplicate current profile for a new profile")];
380 [_textfieldPanel setSubTitleString:_NS("Enter a name for the new profile:")];
381 [_textfieldPanel setCancelButtonString:_NS("Cancel")];
382 [_textfieldPanel setOkButtonString:_NS("Save")];
384 __weak typeof(self) _self = self;
385 [_textfieldPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSString *resultingText) {
387 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
388 if (returnCode != NSOKButton) {
389 [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
393 NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
395 // duplicate names are not allowed in the popup control
396 if ([resultingText length] == 0 || [profileNames containsObject:resultingText]) {
397 [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
399 NSAlert *alert = [[NSAlert alloc] init];
400 [alert setAlertStyle:NSCriticalAlertStyle];
401 [alert setMessageText:_NS("Please enter a unique name for the new profile.")];
402 [alert setInformativeText:_NS("Multiple profiles with the same name are not allowed.")];
404 [alert beginSheetModalForWindow:_self.window
411 NSString *newProfile = [_self generateProfileString];
413 /* add string to user defaults as well as a label */
414 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
415 [workArray addObject:newProfile];
416 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
417 [defaults setInteger:[workArray count] - 1 forKey:@"AudioEffectSelectedProfile"];
418 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
419 [workArray addObject:resultingText];
420 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
423 [defaults synchronize];
424 [_self resetProfileSelector];
429 - (void)removeAudioEffectsProfile:(id)sender
432 [_popupPanel setTitleString:_NS("Remove a preset")];
433 [_popupPanel setSubTitleString:_NS("Select the preset you would like to remove:")];
434 [_popupPanel setOkButtonString:_NS("Remove")];
435 [_popupPanel setCancelButtonString:_NS("Cancel")];
436 [_popupPanel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"AudioEffectProfileNames"]];
438 __weak typeof(self) _self = self;
439 [_popupPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSInteger selectedIndex) {
441 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
442 if (returnCode != NSOKButton) {
443 [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
447 /* remove selected profile from settings */
448 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
449 [workArray removeObjectAtIndex:selectedIndex];
450 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
451 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
452 [workArray removeObjectAtIndex:selectedIndex];
453 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
455 if (i_old_profile_index >= selectedIndex)
456 [defaults setInteger:i_old_profile_index - 1 forKey:@"AudioEffectSelectedProfile"];
459 [defaults synchronize];
460 [_self resetProfileSelector];
465 #pragma mark Equalizer
466 static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
469 char *psz_parser, *psz_string = NULL;
470 audio_output_t *p_aout = getAout();
474 psz_string = var_InheritString(pl_Get(p_custom_intf), "audio-filter");
477 psz_string = var_GetNonEmptyString(p_aout, "audio-filter");
479 vlc_object_release(p_aout);
484 psz_parser = strstr(psz_string, psz_name);
494 - (void)updatePresetSelector
496 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
497 NSArray *presets = [defaults objectForKey:@"EQNames"];
499 [_equalizerPresetsPopup removeAllItems];
500 [_equalizerPresetsPopup addItemsWithTitles:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
501 [[_equalizerPresetsPopup menu] addItem:[NSMenuItem separatorItem]];
502 [_equalizerPresetsPopup addItemWithTitle:_NS("Add new Preset...")];
503 [[_equalizerPresetsPopup lastItem] setTarget: self];
504 [[_equalizerPresetsPopup lastItem] setAction: @selector(addPresetAction:)];
506 if ([presets count] > 1) {
507 [_equalizerPresetsPopup addItemWithTitle:_NS("Organize Presets...")];
508 [[_equalizerPresetsPopup lastItem] setTarget: self];
509 [[_equalizerPresetsPopup lastItem] setAction: @selector(deletePresetAction:)];
512 audio_output_t *p_aout = getAout();
514 NSString *currentPreset = nil;
516 char *psz_preset_string = var_GetNonEmptyString(p_aout, "equalizer-preset");
517 currentPreset = toNSStr(psz_preset_string);
518 free(psz_preset_string);
519 vlc_object_release(p_aout);
522 NSUInteger currentPresetIndex = 0;
523 if (currentPreset && [currentPreset length] > 0) {
524 currentPresetIndex = [presets indexOfObject:currentPreset];
526 if (currentPresetIndex == NSNotFound)
527 currentPresetIndex = [presets count] - 1;
530 [_equalizerPresetsPopup selectItemAtIndex:currentPresetIndex];
531 [self equalizerChangePreset:_equalizerPresetsPopup];
533 [_equalizerPreampSlider setFloatValue:[[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:currentPresetIndex] floatValue]];
534 [self setBandSliderValuesForPreset:currentPresetIndex];
537 - (void)equalizerUpdated
539 intf_thread_t *p_intf = getIntf();
540 bool b_2p = (BOOL)config_GetInt(p_intf, "equalizer-2pass");
541 bool bEnabled = GetEqualizerStatus(p_intf, (char *)"equalizer");
544 [self updatePresetSelector];
546 /* Set the the checkboxes */
547 [_equalizerView enableSubviews: bEnabled];
548 [_equalizerEnableCheckbox setState: bEnabled];
549 [_equalizerTwoPassCheckbox setState: b_2p];
552 - (id)sliderByIndex:(int)index
555 case 0 : return _equalizerBand1Slider;
556 case 1 : return _equalizerBand2Slider;
557 case 2 : return _equalizerBand3Slider;
558 case 3 : return _equalizerBand4Slider;
559 case 4 : return _equalizerBand5Slider;
560 case 5 : return _equalizerBand6Slider;
561 case 6 : return _equalizerBand7Slider;
562 case 7 : return _equalizerBand8Slider;
563 case 8 : return _equalizerBand9Slider;
564 case 9 : return _equalizerBand10Slider;
565 default : return nil;
569 - (void)setBandSliderValuesForPreset:(NSInteger)presetID
571 NSString *preset = [[[NSUserDefaults standardUserDefaults] objectForKey:@"EQValues"] objectAtIndex:presetID];
572 NSArray *values = [preset componentsSeparatedByString:@" "];
573 NSUInteger count = [values count];
574 for (NSUInteger x = 0; x < count; x++)
575 [self setValue:[[values objectAtIndex:x] floatValue] forSlider:x];
578 - (NSString *)generatePresetString
580 return [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
581 [_equalizerBand1Slider floatValue],
582 [_equalizerBand2Slider floatValue],
583 [_equalizerBand3Slider floatValue],
584 [_equalizerBand4Slider floatValue],
585 [_equalizerBand5Slider floatValue],
586 [_equalizerBand6Slider floatValue],
587 [_equalizerBand7Slider floatValue],
588 [_equalizerBand8Slider floatValue],
589 [_equalizerBand9Slider floatValue],
590 [_equalizerBand10Slider floatValue]];
593 - (void)setValue:(float)value forSlider:(int)index
595 id slider = [self sliderByIndex:index];
598 [slider setFloatValue:value];
601 - (IBAction)equalizerEnable:(id)sender
603 [_equalizerView enableSubviews:[sender state]];
604 [self setAudioFilter: "equalizer" on:[sender state]];
607 - (IBAction)equalizerBandSliderUpdated:(id)sender
609 audio_output_t *p_aout = getAout();
611 var_SetString(p_aout, "equalizer-bands", [[self generatePresetString] UTF8String]);
612 vlc_object_release(p_aout);
615 /* save changed to config */
616 config_PutPsz(getIntf(), "equalizer-bands", [[self generatePresetString] UTF8String]);
619 - (IBAction)equalizerChangePreset:(id)sender
621 intf_thread_t *p_intf = getIntf();
622 NSInteger numberOfChosenPreset = [sender indexOfSelectedItem];
623 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
625 NSString *preset = [[defaults objectForKey:@"EQValues"] objectAtIndex:numberOfChosenPreset];
626 NSString *preamp = [[defaults objectForKey:@"EQPreampValues"] objectAtIndex:numberOfChosenPreset];
628 audio_output_t *p_aout = getAout();
630 var_SetString(p_aout, "equalizer-bands", [preset UTF8String]);
631 var_SetFloat(p_aout, "equalizer-preamp", [preamp floatValue]);
632 var_SetString(p_aout, "equalizer-preset" , [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String]);
633 vlc_object_release(p_aout);
636 [_equalizerPreampSlider setFloatValue: [preamp floatValue]];
637 [self setBandSliderValuesForPreset:numberOfChosenPreset];
639 /* save changed to config */
640 config_PutPsz(p_intf, "equalizer-bands", [preset UTF8String]);
641 config_PutFloat(p_intf, "equalizer-preamp", [preamp floatValue]);
642 config_PutPsz(p_intf, "equalizer-preset", [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String]);
645 - (IBAction)equalizerPreAmpSliderUpdated:(id)sender
647 float fPreamp = [sender floatValue] ;
649 audio_output_t *p_aout = getAout();
651 var_SetFloat(p_aout, "equalizer-preamp", fPreamp);
652 vlc_object_release(p_aout);
655 /* save changed to config */
656 config_PutFloat(getIntf(), "equalizer-preamp", fPreamp);
659 - (IBAction)equalizerTwoPass:(id)sender
661 bool b_2p = [sender state] ? true : false;
663 audio_output_t *p_aout = getAout();
665 var_SetBool(p_aout, "equalizer-2pass", b_2p);
666 vlc_object_release(p_aout);
669 /* save changed to config */
670 config_PutInt(getIntf(), "equalizer-2pass", (int)b_2p);
673 - (IBAction)addPresetAction:(id)sender
676 [_textfieldPanel setTitleString:_NS("Save current selection as new preset")];
677 [_textfieldPanel setSubTitleString:_NS("Enter a name for the new preset:")];
678 [_textfieldPanel setCancelButtonString:_NS("Cancel")];
679 [_textfieldPanel setOkButtonString:_NS("Save")];
681 __weak typeof(self) _self = self;
682 [_textfieldPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSString *resultingText) {
683 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
686 if (returnCode != NSOKButton || [resultingText length] == 0)
689 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
690 [workArray addObject:[self generatePresetString]];
691 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
692 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
693 [workArray addObject:resultingText];
694 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
695 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
696 [workArray addObject:[NSString stringWithFormat:@"%.1f", [_equalizerPreampSlider floatValue]]];
697 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
698 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
699 [workArray addObject:[resultingText decomposedStringWithCanonicalMapping]];
700 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
701 [defaults synchronize];
703 /* update VLC internals */
704 audio_output_t *p_aout = getAout();
706 var_SetString(p_aout, "equalizer-preset", [[resultingText decomposedStringWithCanonicalMapping] UTF8String]);
707 vlc_object_release(p_aout);
710 config_PutPsz(getIntf(), "equalizer-preset", [[resultingText decomposedStringWithCanonicalMapping] UTF8String]);
713 [_self updatePresetSelector];
717 - (IBAction)deletePresetAction:(id)sender
719 [_popupPanel setTitleString:_NS("Remove a preset")];
720 [_popupPanel setSubTitleString:_NS("Select the preset you would like to remove:")];
721 [_popupPanel setOkButtonString:_NS("Remove")];
722 [_popupPanel setCancelButtonString:_NS("Cancel")];
723 [_popupPanel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
725 __weak typeof(self) _self = self;
726 [_popupPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSInteger selectedIndex) {
728 if (returnCode != NSOKButton)
731 /* remove requested profile from the arrays */
732 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
733 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
734 [workArray removeObjectAtIndex:selectedIndex];
735 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
736 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
737 [workArray removeObjectAtIndex:selectedIndex];
738 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
739 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
740 [workArray removeObjectAtIndex:selectedIndex];
741 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
742 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
743 [workArray removeObjectAtIndex:selectedIndex];
744 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
745 [defaults synchronize];
748 [_self updatePresetSelector];
753 #pragma mark Compressor
754 - (void)resetCompressor
756 intf_thread_t *p_intf = getIntf();
757 BOOL bEnable_compressor = NO;
759 psz_afilters = var_InheritString(pl_Get(p_intf), "audio-filter");
761 bEnable_compressor = strstr(psz_afilters, "compressor") != NULL;
762 [_compressorEnableCheckbox setState: (NSInteger)strstr(psz_afilters, "compressor") ];
766 [_compressorView enableSubviews:bEnable_compressor];
767 [_compressorEnableCheckbox setState:(bEnable_compressor ? NSOnState : NSOffState)];
769 [_compressorBand1Slider setFloatValue: config_GetFloat(p_intf, "compressor-rms-peak")];
770 [_compressorBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [_compressorBand1Slider floatValue]]];
771 [_compressorBand2Slider setFloatValue: config_GetFloat(p_intf, "compressor-attack")];
772 [_compressorBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [_compressorBand2Slider floatValue]]];
773 [_compressorBand3Slider setFloatValue: config_GetFloat(p_intf, "compressor-release")];
774 [_compressorBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [_compressorBand3Slider floatValue]]];
775 [_compressorBand4Slider setFloatValue: config_GetFloat(p_intf, "compressor-threshold")];
776 [_compressorBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [_compressorBand4Slider floatValue]]];
777 [_compressorBand5Slider setFloatValue: config_GetFloat(p_intf, "compressor-ratio")];
778 [_compressorBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [_compressorBand5Slider floatValue]]];
779 [_compressorBand6Slider setFloatValue: config_GetFloat(p_intf, "compressor-knee")];
780 [_compressorBand6TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [_compressorBand6Slider floatValue]]];
781 [_compressorBand7Slider setFloatValue: config_GetFloat(p_intf, "compressor-makeup-gain")];
782 [_compressorBand7TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [_compressorBand7Slider floatValue]]];
785 - (IBAction)resetCompressorValues:(id)sender
787 intf_thread_t *p_intf = getIntf();
788 config_PutFloat(p_intf, "compressor-rms-peak", 0.000000);
789 config_PutFloat(p_intf, "compressor-attack", 25.000000);
790 config_PutFloat(p_intf, "compressor-release", 100.000000);
791 config_PutFloat(p_intf, "compressor-threshold", -11.000000);
792 config_PutFloat(p_intf, "compressor-ratio", 8.000000);
793 config_PutFloat(p_intf, "compressor-knee", 2.500000);
794 config_PutFloat(p_intf, "compressor-makeup-gain", 7.000000);
796 audio_output_t *p_aout = getAout();
798 var_SetFloat(p_aout, "compressor-rms-peak", 0.000000);
799 var_SetFloat(p_aout, "compressor-attack", 25.000000);
800 var_SetFloat(p_aout, "compressor-release", 100.000000);
801 var_SetFloat(p_aout, "compressor-threshold", -11.000000);
802 var_SetFloat(p_aout, "compressor-ratio", 8.000000);
803 var_SetFloat(p_aout, "compressor-knee", 2.500000);
804 var_SetFloat(p_aout, "compressor-makeup-gain", 7.000000);
805 vlc_object_release(p_aout);
807 [self resetCompressor];
810 - (IBAction)compressorEnable:(id)sender
812 [_compressorView enableSubviews:[sender state]];
813 [self setAudioFilter:"compressor" on:[sender state]];
816 - (IBAction)compressorSliderUpdated:(id)sender
818 audio_output_t *p_aout = getAout();
820 if (sender == _compressorBand1Slider)
821 value = "compressor-rms-peak";
822 else if (sender == _compressorBand2Slider)
823 value = "compressor-attack";
824 else if (sender == _compressorBand3Slider)
825 value = "compressor-release";
826 else if (sender == _compressorBand4Slider)
827 value = "compressor-threshold";
828 else if (sender == _compressorBand5Slider)
829 value = "compressor-ratio";
830 else if (sender == _compressorBand6Slider)
831 value = "compressor-knee";
832 else if (sender == _compressorBand7Slider)
833 value = "compressor-makeup-gain";
836 var_SetFloat(p_aout, value, [sender floatValue]);
837 vlc_object_release(p_aout);
839 config_PutFloat(getIntf(), value, [sender floatValue]);
841 if (sender == _compressorBand1Slider)
842 [_compressorBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
843 else if (sender == _compressorBand2Slider)
844 [_compressorBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [sender floatValue]]];
845 else if (sender == _compressorBand3Slider)
846 [_compressorBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [sender floatValue]]];
847 else if (sender == _compressorBand4Slider)
848 [_compressorBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [sender floatValue]]];
849 else if (sender == _compressorBand5Slider)
850 [_compressorBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [sender floatValue]]];
851 else if (sender == _compressorBand6Slider)
852 [_compressorBand6TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [sender floatValue]]];
853 else if (sender == _compressorBand7Slider)
854 [_compressorBand7TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [sender floatValue]]];
858 #pragma mark Spatializer
859 - (void)resetSpatializer
861 intf_thread_t *p_intf = getIntf();
862 BOOL bEnable_spatializer = NO;
864 psz_afilters = var_InheritString(pl_Get(p_intf), "audio-filter");
866 bEnable_spatializer = strstr(psz_afilters, "spatializer") != NULL;
870 [_spatializerView enableSubviews:bEnable_spatializer];
871 [_spatializerEnableCheckbox setState:(bEnable_spatializer ? NSOnState : NSOffState)];
874 #define setSlider(bandsld, bandfld, var) \
875 [bandsld setFloatValue: config_GetFloat(p_intf, var) * 10.]; \
876 [bandfld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [bandsld floatValue]]]
878 setSlider(_spatializerBand1Slider, _spatializerBand1TextField, "spatializer-roomsize");
879 setSlider(_spatializerBand2Slider, _spatializerBand2TextField, "spatializer-width");
880 setSlider(_spatializerBand3Slider, _spatializerBand3TextField, "spatializer-wet");
881 setSlider(_spatializerBand4Slider, _spatializerBand4TextField, "spatializer-dry");
882 setSlider(_spatializerBand5Slider, _spatializerBand5TextField, "spatializer-damp");
887 - (IBAction)resetSpatializerValues:(id)sender
889 intf_thread_t *p_intf = getIntf();
890 config_PutFloat(p_intf, "spatializer-roomsize", .85);
891 config_PutFloat(p_intf, "spatializer-width", 1.);
892 config_PutFloat(p_intf, "spatializer-wet", .4);
893 config_PutFloat(p_intf, "spatializer-dry", .5);
894 config_PutFloat(p_intf, "spatializer-damp", .5);
896 audio_output_t *p_aout = getAout();
898 var_SetFloat(p_aout, "spatializer-roomsize", .85);
899 var_SetFloat(p_aout, "spatializer-width", 1.);
900 var_SetFloat(p_aout, "spatializer-wet", .4);
901 var_SetFloat(p_aout, "spatializer-dry", .5);
902 var_SetFloat(p_aout, "spatializer-damp", .5);
903 vlc_object_release(p_aout);
905 [self resetSpatializer];
908 - (IBAction)spatializerEnable:(id)sender
910 [_spatializerView enableSubviews:[sender state]];
911 [self setAudioFilter:"spatializer" on:[sender state]];
914 - (IBAction)spatializerSliderUpdated:(id)sender
916 audio_output_t *p_aout = getAout();
918 if (sender == _spatializerBand1Slider)
919 value = "spatializer-roomsize";
920 else if (sender == _spatializerBand2Slider)
921 value = "spatializer-width";
922 else if (sender == _spatializerBand3Slider)
923 value = "spatializer-wet";
924 else if (sender == _spatializerBand4Slider)
925 value = "spatializer-dry";
926 else if (sender == _spatializerBand5Slider)
927 value = "spatializer-damp";
930 var_SetFloat(p_aout, value, [sender floatValue] / 10.);
931 vlc_object_release(p_aout);
933 config_PutFloat(getIntf(), value, [sender floatValue] / 10.);
935 if (sender == _spatializerBand1Slider)
936 [_spatializerBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
937 else if (sender == _spatializerBand2Slider)
938 [_spatializerBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
939 else if (sender == _spatializerBand3Slider)
940 [_spatializerBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
941 else if (sender == _spatializerBand4Slider)
942 [_spatializerBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
943 else if (sender == _spatializerBand5Slider)
944 [_spatializerBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
949 - (void)resetAudioFilters
951 intf_thread_t *p_intf = getIntf();
952 BOOL bEnable_normvol = NO;
954 psz_afilters = var_InheritString(pl_Get(p_intf), "audio-filter");
956 [_filterHeadPhoneCheckbox setState: (NSInteger)strstr(psz_afilters, "headphone") ];
957 [_filterKaraokeCheckbox setState: (NSInteger)strstr(psz_afilters, "karaoke") ];
958 [_filterScaleTempoCheckbox setState: (NSInteger)strstr(psz_afilters, "scaletempo") ];
959 [_filterStereoEnhancerCheckbox setState: (NSInteger)strstr(psz_afilters, "stereo_widen") ];
960 bEnable_normvol = strstr(psz_afilters, "normvol") != NULL;
963 [_filterHeadPhoneCheckbox setState: NSOffState];
964 [_filterKaraokeCheckbox setState: NSOffState];
965 [_filterScaleTempoCheckbox setState: NSOffState];
966 [_filterStereoEnhancerCheckbox setState: NSOffState];
969 [_filterNormLevelSlider setEnabled:bEnable_normvol];
970 [_filterNormLevelLabel setEnabled:bEnable_normvol];
971 [_filterNormLevelCheckbox setState:(bEnable_normvol ? NSOnState : NSOffState)];
973 [_filterNormLevelSlider setFloatValue: config_GetFloat(p_intf, "norm-max-level")];
976 - (IBAction)filterEnableHeadPhoneVirt:(id)sender
978 [self setAudioFilter:"headphone" on:[sender state]];
981 - (IBAction)filterEnableVolumeNorm:(id)sender
983 [_filterNormLevelSlider setEnabled:[sender state]];
984 [_filterNormLevelLabel setEnabled:[sender state]];
985 [self setAudioFilter:"normvol" on:[sender state]];
988 - (IBAction)filterVolumeNormSliderUpdated:(id)sender
990 audio_output_t *p_aout = getAout();
993 var_SetFloat(p_aout, "norm-max-level", [_filterNormLevelSlider floatValue]);
994 vlc_object_release(p_aout);
997 config_PutFloat(getIntf(), "norm-max-level", [_filterNormLevelSlider floatValue]);
1000 - (IBAction)filterEnableKaraoke:(id)sender
1002 [self setAudioFilter:"karaoke" on:[sender state]];
1005 - (IBAction)filterEnableScaleTempo:(id)sender
1007 [self setAudioFilter:"scaletempo" on:[sender state]];
1010 - (IBAction)filterEnableStereoEnhancer:(id)sender
1012 [self setAudioFilter:"stereo_widen" on:[sender state]];