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];
98 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AudioEffectApplyProfileOnStartup"])
100 [self equalizerUpdated];
101 [self resetCompressor];
102 [self resetSpatializer];
103 [self resetAudioFilters];
113 intf_thread_t *p_intf = getIntf();
114 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
115 NSInteger selectedProfile = [defaults integerForKey:@"AudioEffectSelectedProfile"];
116 playlist_t *p_playlist = pl_Get(p_intf);
118 /* disable existing filters */
119 playlist_EnableAudioFilter(p_playlist, "equalizer", false);
120 playlist_EnableAudioFilter(p_playlist, "compressor", false);
121 playlist_EnableAudioFilter(p_playlist, "spatializer", false);
122 playlist_EnableAudioFilter(p_playlist, "compressor", false);
123 playlist_EnableAudioFilter(p_playlist, "headphone", false);
124 playlist_EnableAudioFilter(p_playlist, "normvol", false);
125 playlist_EnableAudioFilter(p_playlist, "karaoke", false);
128 NSArray *items = [[[defaults objectForKey:@"AudioEffectProfiles"] objectAtIndex:(NSUInteger) selectedProfile] componentsSeparatedByString:@";"];
131 char const *psz_eq_preset = [B64DecNSStr([items firstObject]) UTF8String];
132 audio_output_t *p_aout = getAout();
134 var_SetString(p_aout, "equalizer-preset", psz_eq_preset);
135 var_SetString(p_playlist, "equalizer-preset", psz_eq_preset);
137 /* filter handling */
138 NSString *tempString = B64DecNSStr([items objectAtIndex:1]);
141 /* enable the new filters */
142 if ([tempString length] > 0) {
143 tempArray = [tempString componentsSeparatedByString:@":"];
144 count = [tempArray count];
145 for (NSUInteger x = 0; x < count; x++)
146 playlist_EnableAudioFilter(p_playlist, [[tempArray objectAtIndex:x] UTF8String], true);
150 var_SetFloat(p_playlist, "compressor-rms-peak",[[items objectAtIndex:2] floatValue]);
151 var_SetFloat(p_playlist, "compressor-attack",[[items objectAtIndex:3] floatValue]);
152 var_SetFloat(p_playlist, "compressor-release",[[items objectAtIndex:4] floatValue]);
153 var_SetFloat(p_playlist, "compressor-threshold",[[items objectAtIndex:5] floatValue]);
154 var_SetFloat(p_playlist, "compressor-ratio",[[items objectAtIndex:6] floatValue]);
155 var_SetFloat(p_playlist, "compressor-knee",[[items objectAtIndex:7] floatValue]);
156 var_SetFloat(p_playlist, "compressor-makeup-gain",[[items objectAtIndex:8] floatValue]);
157 var_SetFloat(p_playlist, "spatializer-roomsize",[[items objectAtIndex:9] floatValue]);
158 var_SetFloat(p_playlist, "spatializer-width",[[items objectAtIndex:10] floatValue]);
159 var_SetFloat(p_playlist, "spatializer-wet",[[items objectAtIndex:11] floatValue]);
160 var_SetFloat(p_playlist, "spatializer-dry",[[items objectAtIndex:12] floatValue]);
161 var_SetFloat(p_playlist, "spatializer-damp",[[items objectAtIndex:13] floatValue]);
162 var_SetFloat(p_playlist, "norm-max-level",[[items objectAtIndex:14] floatValue]);
163 var_SetBool(p_playlist, "equalizer-2pass",(BOOL)[[items objectAtIndex:15] intValue]);
165 /* set values on-the-fly if we have an aout */
167 var_SetFloat(p_aout, "compressor-rms-peak", [[items objectAtIndex:2] floatValue]);
168 var_SetFloat(p_aout, "compressor-attack", [[items objectAtIndex:3] floatValue]);
169 var_SetFloat(p_aout, "compressor-release", [[items objectAtIndex:4] floatValue]);
170 var_SetFloat(p_aout, "compressor-threshold", [[items objectAtIndex:5] floatValue]);
171 var_SetFloat(p_aout, "compressor-ratio", [[items objectAtIndex:6] floatValue]);
172 var_SetFloat(p_aout, "compressor-knee", [[items objectAtIndex:7] floatValue]);
173 var_SetFloat(p_aout, "compressor-makeup-gain", [[items objectAtIndex:8] floatValue]);
174 var_SetFloat(p_aout, "spatializer-roomsize", [[items objectAtIndex:9] floatValue]);
175 var_SetFloat(p_aout, "spatializer-width", [[items objectAtIndex:10] floatValue]);
176 var_SetFloat(p_aout, "spatializer-wet", [[items objectAtIndex:11] floatValue]);
177 var_SetFloat(p_aout, "spatializer-dry", [[items objectAtIndex:12] floatValue]);
178 var_SetFloat(p_aout, "spatializer-damp", [[items objectAtIndex:13] floatValue]);
179 var_SetFloat(p_aout, "norm-max-level", [[items objectAtIndex:14] floatValue]);
180 var_SetBool(p_aout, "equalizer-2pass", (BOOL)[[items objectAtIndex:15] intValue]);
184 BOOL b_equalizerEnabled = [tempString rangeOfString:@"equalizer"].location != NSNotFound;
185 [_equalizerView enableSubviews:b_equalizerEnabled];
186 [_equalizerEnableCheckbox setState:(b_equalizerEnabled ? NSOnState : NSOffState)];
188 [_equalizerTwoPassCheckbox setState:[[items objectAtIndex:15] intValue]];
190 /* store current profile selection */
191 [defaults setInteger:selectedProfile forKey:@"AudioEffectSelectedProfile"];
192 [defaults synchronize];
195 vlc_object_release(p_aout);
198 - (void)windowDidLoad
200 [_applyProfileCheckbox setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"AudioEffectApplyProfileOnStartup"]];
202 /* setup the user's language */
204 [_equalizerEnableCheckbox setTitle:_NS("Enable")];
205 [_equalizerTwoPassCheckbox setTitle:_NS("2 Pass")];
206 [_equalizerTwoPassCheckbox setToolTip:_NS("Filter the audio twice. This provides a more " \
208 [_equalizerPreampLabel setStringValue:_NS("Preamp")];
209 [_equalizerPreampLabel setToolTip:_NS("Set the global gain in dB (-20 ... 20).")];
212 [_compressorEnableCheckbox setTitle:_NS("Enable dynamic range compressor")];
213 [_compressorResetButton setTitle:_NS("Reset")];
214 [_compressorBand1Label setStringValue:_NS("RMS/peak")];;
215 [_compressorBand2Label setStringValue:_NS("Attack")];
216 [_compressorBand3Label setStringValue:_NS("Release")];
217 [_compressorBand4Label setStringValue:_NS("Threshold")];
218 [_compressorBand5Label setStringValue:_NS("Ratio")];
219 [_compressorBand6Label setStringValue:_NS("Knee radius")];
220 [_compressorBand7Label setStringValue:_NS("Makeup gain")];
223 [_spatializerEnableCheckbox setTitle:_NS("Enable Spatializer")];
224 [_spatializerResetButton setTitle:_NS("Reset")];
225 [_spatializerBand1Label setStringValue:_NS("Size")];
226 [_spatializerBand1Label setToolTip:_NS("Defines the virtual surface of the room" \
227 " emulated by the filter.")];
228 [_spatializerBand2Label setStringValue:_NS("Width")];
229 [_spatializerBand2Label setToolTip:_NS("Width of the virtual room")];
230 [_spatializerBand3Label setStringValue:_NS("Wet")];
231 [_spatializerBand4Label setStringValue:_NS("Dry")];
232 [_spatializerBand5Label setStringValue:_NS("Damp")];
235 [_filterHeadPhoneCheckbox setTitle:_NS("Headphone virtualization")];
236 [_filterHeadPhoneCheckbox setToolTip:_NS("This effect gives you the feeling that you are standing in a room " \
237 "with a complete 7.1 speaker set when using only a headphone, " \
238 "providing a more realistic sound experience. It should also be " \
239 "more comfortable and less tiring when listening to music for " \
240 "long periods of time.\nIt works with any source format from mono " \
242 [_filterNormLevelCheckbox setTitle:_NS("Volume normalization")];
243 [_filterNormLevelCheckbox setToolTip:_NS("Volume normalizer")];
244 [_filterNormLevelLabel setToolTip:_NS("If the average power over the last N buffers " \
245 "is higher than this value, the volume will be normalized. " \
246 "This value is a positive floating point number. A value " \
247 "between 0.5 and 10 seems sensible.")];
248 [_filterNormLevelLabel setStringValue:_NS("Maximum level")];
249 [_filterKaraokeCheckbox setTitle:_NS("Karaoke")];
250 [_filterKaraokeCheckbox setToolTip:_NS("Simple Karaoke filter")];
251 [_filterScaleTempoCheckbox setTitle:_NS("Scaletempo")];
252 [_filterScaleTempoCheckbox setToolTip:_NS("Audio tempo scaler synched with rate")];
253 [_filterStereoEnhancerCheckbox setTitle:_NS("Stereo Enhancer")];
254 [_filterStereoEnhancerCheckbox setToolTip:_NS("This filter enhances the stereo effect by "\
255 "suppressing mono (signal common to both channels) "\
256 "and by delaying the signal of left into right and vice versa, "\
257 "thereby widening the stereo effect.")];
260 [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"equalizer"]] setLabel:_NS("Equalizer")];
261 [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"compressor"]] setLabel:_NS("Compressor")];
262 [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"spatializer"]] setLabel:_NS("Spatializer")];
263 [[_tabView tabViewItemAtIndex:[_tabView indexOfTabViewItemWithIdentifier:@"filter"]] setLabel:_NS("Filter")];
264 [self.window setTitle:_NS("Audio Effects")];
265 [self.window setExcludedFromWindowsMenu:YES];
266 [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
268 [self equalizerUpdated];
269 [self resetCompressor];
270 [self resetSpatializer];
271 [self resetAudioFilters];
272 [self resetProfileSelector];
276 #pragma mark internal functions
278 - (void)setAudioFilter: (char *)psz_name on:(BOOL)b_on
280 playlist_EnableAudioFilter(pl_Get(getIntf()), psz_name, b_on);
283 - (void)resetProfileSelector
285 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
286 [_profilePopup removeAllItems];
288 NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
289 [_profilePopup addItemsWithTitles:profileNames];
291 [[_profilePopup menu] addItem:[NSMenuItem separatorItem]];
292 [_profilePopup addItemWithTitle:_NS("Duplicate current profile...")];
293 [[_profilePopup lastItem] setTarget: self];
294 [[_profilePopup lastItem] setAction: @selector(addAudioEffectsProfile:)];
296 if ([profileNames count] > 1) {
297 [_profilePopup addItemWithTitle:_NS("Organize Profiles...")];
298 [[_profilePopup lastItem] setTarget: self];
299 [[_profilePopup lastItem] setAction: @selector(removeAudioEffectsProfile:)];
302 [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
303 if (i_old_profile_index || [[NSUserDefaults standardUserDefaults] integerForKey:@"AudioEffectSelectedProfile"])
304 [self profileSelectorAction:self];
308 #pragma mark generic code
309 - (void)updateCocoaWindowLevel:(NSInteger)i_level
311 if (self.isWindowLoaded && [self.window isVisible] && [self.window level] != i_level)
312 [self.window setLevel: i_level];
315 - (IBAction)toggleWindow:(id)sender
317 if ([self.window isKeyWindow])
318 [self.window orderOut:sender];
320 [self.window setLevel: [[[VLCMain sharedInstance] voutController] currentStatusWindowLevel]];
321 [self.window makeKeyAndOrderFront:sender];
325 - (NSString *)generateProfileString
327 playlist_t *p_playlist = pl_Get(getIntf());
328 return [NSString stringWithFormat:@"%@;%@;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i",
329 B64EncAndFree(var_GetNonEmptyString(p_playlist, "equalizer-preset")),
330 B64EncAndFree(var_InheritString(p_playlist, "audio-filter")),
331 var_InheritFloat(p_playlist, "compressor-rms-peak"),
332 var_InheritFloat(p_playlist, "compressor-attack"),
333 var_InheritFloat(p_playlist, "compressor-release"),
334 var_InheritFloat(p_playlist, "compressor-threshold"),
335 var_InheritFloat(p_playlist, "compressor-ratio"),
336 var_InheritFloat(p_playlist, "compressor-knee"),
337 var_InheritFloat(p_playlist, "compressor-makeup-gain"),
338 var_InheritFloat(p_playlist, "spatializer-roomsize"),
339 var_InheritFloat(p_playlist, "spatializer-width"),
340 var_InheritFloat(p_playlist, "spatializer-wet"),
341 var_InheritFloat(p_playlist, "spatializer-dry"),
342 var_InheritFloat(p_playlist, "spatializer-damp"),
343 var_InheritFloat(p_playlist, "norm-max-level"),
344 var_InheritBool(p_playlist,"equalizer-2pass")];
347 - (void)saveCurrentProfile
349 if (i_old_profile_index == -1)
352 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
353 /* fetch all the current settings in a uniform string */
354 NSString *newProfile = [self generateProfileString];
356 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
357 if (i_old_profile_index >= [workArray count])
360 [workArray replaceObjectAtIndex:i_old_profile_index withObject:newProfile];
361 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
362 [defaults synchronize];
365 - (IBAction)profileSelectorAction:(id)sender
367 [self saveCurrentProfile];
368 i_old_profile_index = [_profilePopup indexOfSelectedItem];
369 [[NSUserDefaults standardUserDefaults] setInteger:i_old_profile_index forKey:@"AudioEffectSelectedProfile"];
373 [self resetCompressor];
374 [self resetSpatializer];
375 [self resetAudioFilters];
376 [self updatePresetSelector];
379 - (void)addAudioEffectsProfile:(id)sender
382 [_textfieldPanel setTitleString:_NS("Duplicate current profile for a new profile")];
383 [_textfieldPanel setSubTitleString:_NS("Enter a name for the new profile:")];
384 [_textfieldPanel setCancelButtonString:_NS("Cancel")];
385 [_textfieldPanel setOkButtonString:_NS("Save")];
387 __weak typeof(self) _self = self;
388 [_textfieldPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSString *resultingText) {
390 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
391 if (returnCode != NSOKButton) {
392 [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
396 NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
398 // duplicate names are not allowed in the popup control
399 if ([resultingText length] == 0 || [profileNames containsObject:resultingText]) {
400 [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
402 NSAlert *alert = [[NSAlert alloc] init];
403 [alert setAlertStyle:NSCriticalAlertStyle];
404 [alert setMessageText:_NS("Please enter a unique name for the new profile.")];
405 [alert setInformativeText:_NS("Multiple profiles with the same name are not allowed.")];
407 [alert beginSheetModalForWindow:_self.window
414 NSString *newProfile = [_self generateProfileString];
416 /* add string to user defaults as well as a label */
417 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
418 [workArray addObject:newProfile];
419 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
420 [defaults setInteger:[workArray count] - 1 forKey:@"AudioEffectSelectedProfile"];
421 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
422 [workArray addObject:resultingText];
423 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
426 [defaults synchronize];
427 [_self resetProfileSelector];
432 - (void)removeAudioEffectsProfile:(id)sender
435 [_popupPanel setTitleString:_NS("Remove a preset")];
436 [_popupPanel setSubTitleString:_NS("Select the preset you would like to remove:")];
437 [_popupPanel setOkButtonString:_NS("Remove")];
438 [_popupPanel setCancelButtonString:_NS("Cancel")];
439 [_popupPanel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"AudioEffectProfileNames"]];
441 __weak typeof(self) _self = self;
442 [_popupPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSInteger selectedIndex) {
444 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
445 if (returnCode != NSOKButton) {
446 [_profilePopup selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
450 /* remove selected profile from settings */
451 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
452 [workArray removeObjectAtIndex:selectedIndex];
453 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
454 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
455 [workArray removeObjectAtIndex:selectedIndex];
456 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
458 if (i_old_profile_index >= selectedIndex)
459 [defaults setInteger:i_old_profile_index - 1 forKey:@"AudioEffectSelectedProfile"];
462 [defaults synchronize];
463 [_self resetProfileSelector];
467 - (IBAction)applyProfileCheckboxChanged:(id)sender
469 [[NSUserDefaults standardUserDefaults] setBool:[sender state] forKey:@"AudioEffectApplyProfileOnStartup"];
473 #pragma mark Equalizer
474 static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
477 char *psz_parser, *psz_string = NULL;
478 audio_output_t *p_aout = getAout();
482 psz_string = var_InheritString(pl_Get(p_custom_intf), "audio-filter");
485 psz_string = var_GetNonEmptyString(p_aout, "audio-filter");
487 vlc_object_release(p_aout);
492 psz_parser = strstr(psz_string, psz_name);
502 - (void)updatePresetSelector
504 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
505 NSArray *presets = [defaults objectForKey:@"EQNames"];
507 [_equalizerPresetsPopup removeAllItems];
508 [_equalizerPresetsPopup addItemsWithTitles:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
509 [[_equalizerPresetsPopup menu] addItem:[NSMenuItem separatorItem]];
510 [_equalizerPresetsPopup addItemWithTitle:_NS("Add new Preset...")];
511 [[_equalizerPresetsPopup lastItem] setTarget: self];
512 [[_equalizerPresetsPopup lastItem] setAction: @selector(addPresetAction:)];
514 if ([presets count] > 1) {
515 [_equalizerPresetsPopup addItemWithTitle:_NS("Organize Presets...")];
516 [[_equalizerPresetsPopup lastItem] setTarget: self];
517 [[_equalizerPresetsPopup lastItem] setAction: @selector(deletePresetAction:)];
520 audio_output_t *p_aout = getAout();
522 NSString *currentPreset = nil;
524 char *psz_preset_string = var_GetNonEmptyString(p_aout, "equalizer-preset");
525 currentPreset = toNSStr(psz_preset_string);
526 free(psz_preset_string);
527 vlc_object_release(p_aout);
530 NSUInteger currentPresetIndex = 0;
531 if (currentPreset && [currentPreset length] > 0) {
532 currentPresetIndex = [presets indexOfObject:currentPreset];
534 if (currentPresetIndex == NSNotFound)
535 currentPresetIndex = [presets count] - 1;
538 [_equalizerPresetsPopup selectItemAtIndex:currentPresetIndex];
539 [self equalizerChangePreset:_equalizerPresetsPopup];
541 [_equalizerPreampSlider setFloatValue:[[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:currentPresetIndex] floatValue]];
542 [self setBandSliderValuesForPreset:currentPresetIndex];
545 - (void)equalizerUpdated
547 intf_thread_t *p_intf = getIntf();
548 playlist_t *p_playlist = pl_Get(p_intf);
549 bool b_2p = var_CreateGetBool(p_playlist, "equalizer-2pass");
550 bool bEnabled = GetEqualizerStatus(p_intf, (char *)"equalizer");
553 var_Create(p_playlist, "equalizer-preset",
554 VLC_VAR_STRING | VLC_VAR_DOINHERIT);
555 [self updatePresetSelector];
557 /* Set the the checkboxes */
558 [_equalizerView enableSubviews: bEnabled];
559 [_equalizerEnableCheckbox setState: bEnabled];
560 [_equalizerTwoPassCheckbox setState: b_2p];
563 - (id)sliderByIndex:(int)index
566 case 0 : return _equalizerBand1Slider;
567 case 1 : return _equalizerBand2Slider;
568 case 2 : return _equalizerBand3Slider;
569 case 3 : return _equalizerBand4Slider;
570 case 4 : return _equalizerBand5Slider;
571 case 5 : return _equalizerBand6Slider;
572 case 6 : return _equalizerBand7Slider;
573 case 7 : return _equalizerBand8Slider;
574 case 8 : return _equalizerBand9Slider;
575 case 9 : return _equalizerBand10Slider;
576 default : return nil;
580 - (void)setBandSliderValuesForPreset:(NSInteger)presetID
582 NSString *preset = [[[NSUserDefaults standardUserDefaults] objectForKey:@"EQValues"] objectAtIndex:presetID];
583 NSArray *values = [preset componentsSeparatedByString:@" "];
584 NSUInteger count = [values count];
585 for (NSUInteger x = 0; x < count; x++)
586 [self setValue:[[values objectAtIndex:x] floatValue] forSlider:x];
589 - (NSString *)generatePresetString
591 return [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
592 [_equalizerBand1Slider floatValue],
593 [_equalizerBand2Slider floatValue],
594 [_equalizerBand3Slider floatValue],
595 [_equalizerBand4Slider floatValue],
596 [_equalizerBand5Slider floatValue],
597 [_equalizerBand6Slider floatValue],
598 [_equalizerBand7Slider floatValue],
599 [_equalizerBand8Slider floatValue],
600 [_equalizerBand9Slider floatValue],
601 [_equalizerBand10Slider floatValue]];
604 - (void)setValue:(float)value forSlider:(int)index
606 id slider = [self sliderByIndex:index];
609 [slider setFloatValue:value];
612 - (IBAction)equalizerEnable:(id)sender
614 [_equalizerView enableSubviews:[sender state]];
615 [self setAudioFilter: "equalizer" on:[sender state]];
618 - (IBAction)equalizerBandSliderUpdated:(id)sender
620 audio_output_t *p_aout = getAout();
621 char const *psz_preset_values = [[self generatePresetString] UTF8String];
623 var_SetString(p_aout, "equalizer-bands", psz_preset_values);
624 vlc_object_release(p_aout);
628 - (IBAction)equalizerChangePreset:(id)sender
630 NSInteger numberOfChosenPreset = [sender indexOfSelectedItem];
631 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
633 char const *psz_eq_bands = [[[defaults objectForKey:@"EQValues"] objectAtIndex:numberOfChosenPreset] UTF8String];
634 float f_eq_preamp = [[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:numberOfChosenPreset] floatValue];
635 char const *psz_eq_preset = [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String];
637 audio_output_t *p_aout = getAout();
639 var_SetString(p_aout, "equalizer-bands", psz_eq_bands);
640 var_SetFloat(p_aout, "equalizer-preamp", f_eq_preamp);
641 var_SetString(p_aout, "equalizer-preset" , psz_eq_preset);
642 vlc_object_release(p_aout);
645 [_equalizerPreampSlider setFloatValue: f_eq_preamp];
646 [self setBandSliderValuesForPreset:numberOfChosenPreset];
648 var_SetString(pl_Get(getIntf()), "equalizer-preset", psz_eq_preset);
651 - (IBAction)equalizerPreAmpSliderUpdated:(id)sender
653 float fPreamp = [sender floatValue] ;
655 audio_output_t *p_aout = getAout();
657 var_SetFloat(p_aout, "equalizer-preamp", fPreamp);
658 vlc_object_release(p_aout);
662 - (IBAction)equalizerTwoPass:(id)sender
664 bool b_2p = [sender state] ? true : false;
666 audio_output_t *p_aout = getAout();
668 var_SetBool(p_aout, "equalizer-2pass", b_2p);
669 vlc_object_release(p_aout);
672 var_SetBool(pl_Get(getIntf()), "equalizer-2pass", b_2p);
675 - (IBAction)addPresetAction:(id)sender
678 [_textfieldPanel setTitleString:_NS("Save current selection as new preset")];
679 [_textfieldPanel setSubTitleString:_NS("Enter a name for the new preset:")];
680 [_textfieldPanel setCancelButtonString:_NS("Cancel")];
681 [_textfieldPanel setOkButtonString:_NS("Save")];
683 __weak typeof(self) _self = self;
684 [_textfieldPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSString *resultingText) {
685 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
688 if (returnCode != NSOKButton || [resultingText length] == 0)
691 NSString *decomposedStringWithCanonicalMapping = [resultingText decomposedStringWithCanonicalMapping];
692 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
693 [workArray addObject:[self generatePresetString]];
694 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
695 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
696 [workArray addObject:resultingText];
697 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
698 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
699 [workArray addObject:[NSString stringWithFormat:@"%.1f", [_equalizerPreampSlider floatValue]]];
700 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
701 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
702 [workArray addObject:decomposedStringWithCanonicalMapping];
703 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
704 [defaults synchronize];
706 /* update VLC internals */
707 char const *psz_eq_preset = [decomposedStringWithCanonicalMapping UTF8String];
708 audio_output_t *p_aout = getAout();
710 var_SetString(p_aout, "equalizer-preset", psz_eq_preset);
711 vlc_object_release(p_aout);
714 var_SetString(pl_Get(getIntf()), "equalizer-preset", psz_eq_preset);
717 [_self updatePresetSelector];
721 - (IBAction)deletePresetAction:(id)sender
723 [_popupPanel setTitleString:_NS("Remove a preset")];
724 [_popupPanel setSubTitleString:_NS("Select the preset you would like to remove:")];
725 [_popupPanel setOkButtonString:_NS("Remove")];
726 [_popupPanel setCancelButtonString:_NS("Cancel")];
727 [_popupPanel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
729 __weak typeof(self) _self = self;
730 [_popupPanel runModalForWindow:self.window completionHandler:^(NSInteger returnCode, NSInteger selectedIndex) {
732 if (returnCode != NSOKButton)
735 /* remove requested profile from the arrays */
736 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
737 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
738 [workArray removeObjectAtIndex:selectedIndex];
739 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
740 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
741 [workArray removeObjectAtIndex:selectedIndex];
742 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
743 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
744 [workArray removeObjectAtIndex:selectedIndex];
745 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
746 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
747 [workArray removeObjectAtIndex:selectedIndex];
748 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
749 [defaults synchronize];
752 [_self updatePresetSelector];
757 #pragma mark Compressor
758 - (void)resetCompressor
760 intf_thread_t *p_intf = getIntf();
761 playlist_t *p_playlist = pl_Get(p_intf);
762 BOOL bEnable_compressor = NO;
764 psz_afilters = var_InheritString(p_playlist, "audio-filter");
766 bEnable_compressor = strstr(psz_afilters, "compressor") != NULL;
767 [_compressorEnableCheckbox setState: (NSInteger)strstr(psz_afilters, "compressor") ];
771 [_compressorView enableSubviews:bEnable_compressor];
772 [_compressorEnableCheckbox setState:(bEnable_compressor ? NSOnState : NSOffState)];
774 [_compressorBand1Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-rms-peak")];
775 [_compressorBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [_compressorBand1Slider floatValue]]];
776 [_compressorBand2Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-attack")];
777 [_compressorBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [_compressorBand2Slider floatValue]]];
778 [_compressorBand3Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-release")];
779 [_compressorBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [_compressorBand3Slider floatValue]]];
780 [_compressorBand4Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-threshold")];
781 [_compressorBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [_compressorBand4Slider floatValue]]];
782 [_compressorBand5Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-ratio")];
783 [_compressorBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [_compressorBand5Slider floatValue]]];
784 [_compressorBand6Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-knee")];
785 [_compressorBand6TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [_compressorBand6Slider floatValue]]];
786 [_compressorBand7Slider setFloatValue: var_CreateGetFloat(p_playlist, "compressor-makeup-gain")];
787 [_compressorBand7TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [_compressorBand7Slider floatValue]]];
790 - (IBAction)resetCompressorValues:(id)sender
792 playlist_t *p_playlist = pl_Get(getIntf());
793 var_SetFloat(p_playlist, "compressor-rms-peak", 0.000000);
794 var_SetFloat(p_playlist, "compressor-attack", 25.000000);
795 var_SetFloat(p_playlist, "compressor-release", 100.000000);
796 var_SetFloat(p_playlist, "compressor-threshold", -11.000000);
797 var_SetFloat(p_playlist, "compressor-ratio", 8.000000);
798 var_SetFloat(p_playlist, "compressor-knee", 2.500000);
799 var_SetFloat(p_playlist, "compressor-makeup-gain", 7.000000);
801 audio_output_t *p_aout = getAout();
803 var_SetFloat(p_aout, "compressor-rms-peak", 0.000000);
804 var_SetFloat(p_aout, "compressor-attack", 25.000000);
805 var_SetFloat(p_aout, "compressor-release", 100.000000);
806 var_SetFloat(p_aout, "compressor-threshold", -11.000000);
807 var_SetFloat(p_aout, "compressor-ratio", 8.000000);
808 var_SetFloat(p_aout, "compressor-knee", 2.500000);
809 var_SetFloat(p_aout, "compressor-makeup-gain", 7.000000);
810 vlc_object_release(p_aout);
812 [self resetCompressor];
815 - (IBAction)compressorEnable:(id)sender
817 [_compressorView enableSubviews:[sender state]];
818 [self setAudioFilter:"compressor" on:[sender state]];
821 - (IBAction)compressorSliderUpdated:(id)sender
823 audio_output_t *p_aout = getAout();
825 float f_value = [sender floatValue];
827 if (sender == _compressorBand1Slider)
828 psz_property = "compressor-rms-peak";
829 else if (sender == _compressorBand2Slider)
830 psz_property = "compressor-attack";
831 else if (sender == _compressorBand3Slider)
832 psz_property = "compressor-release";
833 else if (sender == _compressorBand4Slider)
834 psz_property = "compressor-threshold";
835 else if (sender == _compressorBand5Slider)
836 psz_property = "compressor-ratio";
837 else if (sender == _compressorBand6Slider)
838 psz_property = "compressor-knee";
839 else if (sender == _compressorBand7Slider)
840 psz_property = "compressor-makeup-gain";
843 var_SetFloat(p_aout, psz_property, f_value);
844 vlc_object_release(p_aout);
846 var_SetFloat(pl_Get(getIntf()), psz_property, f_value);
848 if (sender == _compressorBand1Slider)
849 [_compressorBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
850 else if (sender == _compressorBand2Slider)
851 [_compressorBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", f_value]];
852 else if (sender == _compressorBand3Slider)
853 [_compressorBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", f_value]];
854 else if (sender == _compressorBand4Slider)
855 [_compressorBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", f_value]];
856 else if (sender == _compressorBand5Slider)
857 [_compressorBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", f_value]];
858 else if (sender == _compressorBand6Slider)
859 [_compressorBand6TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", f_value]];
860 else if (sender == _compressorBand7Slider)
861 [_compressorBand7TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", f_value]];
865 #pragma mark Spatializer
866 - (void)resetSpatializer
868 playlist_t *p_playlist = pl_Get(getIntf());
869 BOOL bEnable_spatializer = NO;
871 psz_afilters = var_InheritString(p_playlist, "audio-filter");
873 bEnable_spatializer = strstr(psz_afilters, "spatializer") != NULL;
877 [_spatializerView enableSubviews:bEnable_spatializer];
878 [_spatializerEnableCheckbox setState:(bEnable_spatializer ? NSOnState : NSOffState)];
881 #define setSlider(bandsld, bandfld, var) \
882 [bandsld setFloatValue: var_CreateGetFloat(p_playlist, var) * 10.]; \
883 [bandfld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [bandsld floatValue]]]
885 setSlider(_spatializerBand1Slider, _spatializerBand1TextField, "spatializer-roomsize");
886 setSlider(_spatializerBand2Slider, _spatializerBand2TextField, "spatializer-width");
887 setSlider(_spatializerBand3Slider, _spatializerBand3TextField, "spatializer-wet");
888 setSlider(_spatializerBand4Slider, _spatializerBand4TextField, "spatializer-dry");
889 setSlider(_spatializerBand5Slider, _spatializerBand5TextField, "spatializer-damp");
894 - (IBAction)resetSpatializerValues:(id)sender
896 playlist_t *p_playlist = pl_Get(getIntf());
897 var_SetFloat(p_playlist, "spatializer-roomsize", .85);
898 var_SetFloat(p_playlist, "spatializer-width", 1.);
899 var_SetFloat(p_playlist, "spatializer-wet", .4);
900 var_SetFloat(p_playlist, "spatializer-dry", .5);
901 var_SetFloat(p_playlist, "spatializer-damp", .5);
903 audio_output_t *p_aout = getAout();
905 var_SetFloat(p_aout, "spatializer-roomsize", .85);
906 var_SetFloat(p_aout, "spatializer-width", 1.);
907 var_SetFloat(p_aout, "spatializer-wet", .4);
908 var_SetFloat(p_aout, "spatializer-dry", .5);
909 var_SetFloat(p_aout, "spatializer-damp", .5);
910 vlc_object_release(p_aout);
912 [self resetSpatializer];
915 - (IBAction)spatializerEnable:(id)sender
917 [_spatializerView enableSubviews:[sender state]];
918 [self setAudioFilter:"spatializer" on:[sender state]];
921 - (IBAction)spatializerSliderUpdated:(id)sender
923 audio_output_t *p_aout = getAout();
924 char *psz_property = NULL;
925 float f_value = [sender floatValue];
927 if (sender == _spatializerBand1Slider)
928 psz_property = "spatializer-roomsize";
929 else if (sender == _spatializerBand2Slider)
930 psz_property = "spatializer-width";
931 else if (sender == _spatializerBand3Slider)
932 psz_property = "spatializer-wet";
933 else if (sender == _spatializerBand4Slider)
934 psz_property = "spatializer-dry";
935 else if (sender == _spatializerBand5Slider)
936 psz_property = "spatializer-damp";
939 var_SetFloat(p_aout, psz_property, f_value / 10.f);
940 vlc_object_release(p_aout);
942 var_SetFloat(pl_Get(getIntf()), psz_property, f_value / 10.f);
944 if (sender == _spatializerBand1Slider)
945 [_spatializerBand1TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
946 else if (sender == _spatializerBand2Slider)
947 [_spatializerBand2TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
948 else if (sender == _spatializerBand3Slider)
949 [_spatializerBand3TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
950 else if (sender == _spatializerBand4Slider)
951 [_spatializerBand4TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
952 else if (sender == _spatializerBand5Slider)
953 [_spatializerBand5TextField setStringValue:[NSString localizedStringWithFormat:@"%1.1f", f_value]];
958 - (void)resetAudioFilters
960 playlist_t *p_playlist = pl_Get(getIntf());
961 BOOL bEnable_normvol = NO;
963 psz_afilters = var_InheritString(p_playlist, "audio-filter");
965 [_filterHeadPhoneCheckbox setState: (NSInteger)strstr(psz_afilters, "headphone") ];
966 [_filterKaraokeCheckbox setState: (NSInteger)strstr(psz_afilters, "karaoke") ];
967 [_filterScaleTempoCheckbox setState: (NSInteger)strstr(psz_afilters, "scaletempo") ];
968 [_filterStereoEnhancerCheckbox setState: (NSInteger)strstr(psz_afilters, "stereo_widen") ];
969 bEnable_normvol = strstr(psz_afilters, "normvol") != NULL;
972 [_filterHeadPhoneCheckbox setState: NSOffState];
973 [_filterKaraokeCheckbox setState: NSOffState];
974 [_filterScaleTempoCheckbox setState: NSOffState];
975 [_filterStereoEnhancerCheckbox setState: NSOffState];
978 [_filterNormLevelSlider setEnabled:bEnable_normvol];
979 [_filterNormLevelLabel setEnabled:bEnable_normvol];
980 [_filterNormLevelCheckbox setState:(bEnable_normvol ? NSOnState : NSOffState)];
982 [_filterNormLevelSlider setFloatValue: var_CreateGetFloat(p_playlist, "norm-max-level")];
985 - (IBAction)filterEnableHeadPhoneVirt:(id)sender
987 [self setAudioFilter:"headphone" on:[sender state]];
990 - (IBAction)filterEnableVolumeNorm:(id)sender
992 [_filterNormLevelSlider setEnabled:[sender state]];
993 [_filterNormLevelLabel setEnabled:[sender state]];
994 [self setAudioFilter:"normvol" on:[sender state]];
997 - (IBAction)filterVolumeNormSliderUpdated:(id)sender
999 audio_output_t *p_aout = getAout();
1000 float f_value = [_filterNormLevelSlider floatValue];
1003 var_SetFloat(p_aout, "norm-max-level", f_value);
1004 vlc_object_release(p_aout);
1007 var_SetFloat(pl_Get(getIntf()), "norm-max-level", f_value);
1010 - (IBAction)filterEnableKaraoke:(id)sender
1012 [self setAudioFilter:"karaoke" on:[sender state]];
1015 - (IBAction)filterEnableScaleTempo:(id)sender
1017 [self setAudioFilter:"scaletempo" on:[sender state]];
1020 - (IBAction)filterEnableStereoEnhancer:(id)sender
1022 [self setAudioFilter:"stereo_widen" on:[sender state]];