1 /*****************************************************************************
2 * AudioEffects.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2004-2012 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 "AudioEffects.h"
31 #import "../../audio_filter/equalizer_presets.h"
32 #import "CompatibilityFixes.h"
33 #import "SharedDialogs.h"
35 #import <vlc_common.h>
36 #import <vlc_strings.h>
40 @interface VLCAudioEffects (Internal)
41 - (void)resetProfileSelector;
42 - (void)updatePresetSelector;
43 - (void)setBandSliderValuesForPreset:(NSInteger)presetID;
47 #pragma mark Initialization
49 @implementation VLCAudioEffects
50 static VLCAudioEffects *_o_sharedInstance = nil;
52 + (VLCAudioEffects *)sharedInstance
54 return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
58 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
61 NSMutableArray *workValues = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
62 NSMutableArray *workPreamp = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
63 NSMutableArray *workTitles = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
64 NSMutableArray *workNames = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
66 for (int i = 0 ; i < NB_PRESETS ; i++) {
67 workString = [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
68 eqz_preset_10b[i].f_amp[0],
69 eqz_preset_10b[i].f_amp[1],
70 eqz_preset_10b[i].f_amp[2],
71 eqz_preset_10b[i].f_amp[3],
72 eqz_preset_10b[i].f_amp[4],
73 eqz_preset_10b[i].f_amp[5],
74 eqz_preset_10b[i].f_amp[6],
75 eqz_preset_10b[i].f_amp[7],
76 eqz_preset_10b[i].f_amp[8],
77 eqz_preset_10b[i].f_amp[9]];
78 [workValues addObject:workString];
79 [workPreamp addObject:[NSString stringWithFormat:@"%1.f", eqz_preset_10b[i].f_preamp]];
80 [workTitles addObject:[NSString stringWithUTF8String:preset_list_text[i]]];
81 [workNames addObject:[NSString stringWithUTF8String:preset_list[i]]];
84 NSString *defaultProfile = [NSString stringWithFormat:@"ZmxhdA==;;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i",
85 .0,25.,100.,-11.,8.,2.5,7.,.85,1.,.4,.5,.5,2.,0];
87 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];
88 [defaults registerDefaults:appDefaults];
98 if (_o_sharedInstance)
102 i_old_profile_index = -1;
103 _o_sharedInstance = [super init];
106 return _o_sharedInstance;
111 /* setup the user's language */
113 [o_eq_enable_ckb setTitle:_NS("Enable")];
114 [o_eq_twopass_ckb setTitle:_NS("2 Pass")];
115 [o_eq_preamp_lbl setStringValue:_NS("Preamp")];
118 [o_comp_enable_ckb setTitle:_NS("Enable dynamic range compressor")];
119 [o_comp_reset_btn setTitle:_NS("Reset")];
120 [o_comp_band1_lbl setStringValue:_NS("RMS/peak")];;
121 [o_comp_band2_lbl setStringValue:_NS("Attack")];
122 [o_comp_band3_lbl setStringValue:_NS("Release")];
123 [o_comp_band4_lbl setStringValue:_NS("Threshold")];
124 [o_comp_band5_lbl setStringValue:_NS("Ratio")];
125 [o_comp_band6_lbl setStringValue:_NS("Knee radius")];
126 [o_comp_band7_lbl setStringValue:_NS("Makeup gain")];
129 [o_spat_enable_ckb setTitle:_NS("Enable Spatializer")];
130 [o_spat_reset_btn setTitle:_NS("Reset")];
131 [o_spat_band1_lbl setStringValue:_NS("Size")];
132 [o_spat_band2_lbl setStringValue:_NS("Width")];
133 [o_spat_band3_lbl setStringValue:_NS("Wet")];
134 [o_spat_band4_lbl setStringValue:_NS("Dry")];
135 [o_spat_band5_lbl setStringValue:_NS("Damp")];
138 [o_filter_headPhone_ckb setTitle:_NS("Headphone virtualization")];
139 [o_filter_normLevel_ckb setTitle:_NS("Volume normalization")];
140 [o_filter_normLevel_lbl setStringValue:_NS("Maximum level")];
141 [o_filter_karaoke_ckb setTitle:_NS("Karaoke")];
144 [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"equalizer"]] setLabel:_NS("Equalizer")];
145 [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"compressor"]] setLabel:_NS("Compressor")];
146 [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"spatializer"]] setLabel:_NS("Spatializer")];
147 [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"filter"]] setLabel:_NS("Filter")];
148 [o_window setTitle:_NS("Audio Effects")];
149 [o_window setExcludedFromWindowsMenu:YES];
150 if (!OSX_SNOW_LEOPARD)
151 [o_window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
153 [self setupEqualizer];
154 [self resetCompressor];
155 [self resetSpatializer];
156 [self resetAudioFilters];
157 [self resetProfileSelector];
161 #pragma mark internal functions
163 - (void)setAudioFilter: (char *)psz_name on:(BOOL)b_on
166 audio_output_t *p_aout = getAout();
168 psz_tmp = var_GetNonEmptyString(p_aout, "audio-filter");
170 psz_tmp = config_GetPsz(p_intf, "audio-filter");
174 config_PutPsz(p_intf, "audio-filter", psz_name);
175 else if (strstr(psz_tmp, psz_name) == NULL) {
176 psz_tmp = (char *)[[NSString stringWithFormat: @"%s:%s", psz_tmp, psz_name] UTF8String];
177 config_PutPsz(p_intf, "audio-filter", psz_tmp);
181 psz_tmp = (char *)[[[NSString stringWithUTF8String: psz_tmp] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@":%s",psz_name]]] UTF8String];
182 psz_tmp = (char *)[[[NSString stringWithUTF8String: psz_tmp] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@"%s:",psz_name]]] UTF8String];
183 psz_tmp = (char *)[[[NSString stringWithUTF8String: psz_tmp] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithUTF8String:psz_name]]] UTF8String];
184 config_PutPsz(p_intf, "audio-filter", psz_tmp);
189 playlist_EnableAudioFilter(pl_Get(p_intf), psz_name, b_on);
190 vlc_object_release(p_aout);
194 - (void)resetProfileSelector
196 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
197 [o_profile_pop removeAllItems];
199 NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
200 [o_profile_pop addItemsWithTitles:profileNames];
202 [[o_profile_pop menu] addItem:[NSMenuItem separatorItem]];
203 [o_profile_pop addItemWithTitle:_NS("Duplicate current profile...")];
204 [[o_profile_pop lastItem] setTarget: self];
205 [[o_profile_pop lastItem] setAction: @selector(addAudioEffectsProfile:)];
207 if ([profileNames count] > 1) {
208 [o_profile_pop addItemWithTitle:_NS("Organize Profiles...")];
209 [[o_profile_pop lastItem] setTarget: self];
210 [[o_profile_pop lastItem] setAction: @selector(removeAudioEffectsProfile:)];
213 [o_profile_pop selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
214 [self profileSelectorAction:self];
218 #pragma mark generic code
219 - (void)updateCocoaWindowLevel:(NSInteger)i_level
221 if (o_window && [o_window isVisible] && [o_window level] != i_level)
222 [o_window setLevel: i_level];
225 - (IBAction)toggleWindow:(id)sender
227 if ([o_window isVisible])
228 [o_window orderOut:sender];
230 [o_window setLevel: [[[VLCMain sharedInstance] voutController] currentWindowLevel]];
231 [o_window makeKeyAndOrderFront:sender];
235 - (NSString *)generateProfileString
237 vlc_object_t *p_object = VLC_OBJECT(getAout());
238 if (p_object == NULL)
239 p_object = vlc_object_hold(pl_Get(p_intf));
241 NSString *o_str = [NSString stringWithFormat:@"%s;%s;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%lli",
242 vlc_b64_encode(var_GetNonEmptyString(p_object, "equalizer-preset")),
243 vlc_b64_encode(config_GetPsz(p_intf, "audio-filter")),
244 config_GetFloat(p_intf, "compressor-rms-peak"),
245 config_GetFloat(p_intf, "compressor-attack"),
246 config_GetFloat(p_intf, "compressor-release"),
247 config_GetFloat(p_intf, "compressor-threshold"),
248 config_GetFloat(p_intf, "compressor-ratio"),
249 config_GetFloat(p_intf, "compressor-knee"),
250 config_GetFloat(p_intf, "compressor-makeup-gain"),
251 config_GetFloat(p_intf, "spatializer-roomsize"),
252 config_GetFloat(p_intf, "spatializer-width"),
253 config_GetFloat(p_intf, "spatializer-wet"),
254 config_GetFloat(p_intf, "spatializer-dry"),
255 config_GetFloat(p_intf, "spatializer-damp"),
256 config_GetFloat(p_intf, "norm-max-level"),
257 config_GetInt(p_intf,"equalizer-2pass")];
259 vlc_object_release(p_object);
263 - (void)saveCurrentProfile
265 if (i_old_profile_index == -1)
268 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
269 /* fetch all the current settings in a uniform string */
270 NSString *newProfile = [self generateProfileString];
272 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
273 if (i_old_profile_index >= [workArray count])
276 [workArray replaceObjectAtIndex:i_old_profile_index withObject:newProfile];
277 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
279 [defaults synchronize];
282 - (IBAction)profileSelectorAction:(id)sender
284 [self saveCurrentProfile];
285 i_old_profile_index = [o_profile_pop indexOfSelectedItem];
287 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
288 NSUInteger selectedProfile = [o_profile_pop indexOfSelectedItem];
290 audio_output_t *p_aout = getAout();
291 playlist_t *p_playlist = pl_Get(p_intf);
294 /* disable existing filters */
295 playlist_EnableAudioFilter(p_playlist, "equalizer", false);
296 playlist_EnableAudioFilter(p_playlist, "compressor", false);
297 playlist_EnableAudioFilter(p_playlist, "spatializer", false);
298 playlist_EnableAudioFilter(p_playlist, "compressor", false);
299 playlist_EnableAudioFilter(p_playlist, "headphone", false);
300 playlist_EnableAudioFilter(p_playlist, "normvol", false);
301 playlist_EnableAudioFilter(p_playlist, "karaoke", false);
305 NSArray *items = [[[defaults objectForKey:@"AudioEffectProfiles"] objectAtIndex:selectedProfile] componentsSeparatedByString:@";"];
308 vlc_object_t *p_object = VLC_OBJECT(getAout());
309 if (p_object == NULL)
310 p_object = vlc_object_hold(pl_Get(p_intf));
311 var_SetString(p_object,"equalizer-preset",vlc_b64_decode([[items objectAtIndex:0] UTF8String]));
312 vlc_object_release(p_object);
314 /* filter handling */
315 NSString *tempString = [NSString stringWithFormat:@"%s", vlc_b64_decode([[items objectAtIndex:1] UTF8String])];
318 /* enable the new filters, if we have an aout */
320 if ([tempString length] > 0) {
321 tempArray = [tempString componentsSeparatedByString:@":"];
322 count = [tempArray count];
323 for (NSUInteger x = 0; x < count; x++)
324 playlist_EnableAudioFilter(p_playlist, (char *)[[tempArray objectAtIndex:x] UTF8String], true);
327 config_PutPsz(p_intf,"audio-filter",[tempString UTF8String]);
330 config_PutFloat(p_intf, "compressor-rms-peak",[[items objectAtIndex:2] floatValue]);
331 config_PutFloat(p_intf, "compressor-attack",[[items objectAtIndex:3] floatValue]);
332 config_PutFloat(p_intf, "compressor-release",[[items objectAtIndex:4] floatValue]);
333 config_PutFloat(p_intf, "compressor-threshold",[[items objectAtIndex:5] floatValue]);
334 config_PutFloat(p_intf, "compressor-ratio",[[items objectAtIndex:6] floatValue]);
335 config_PutFloat(p_intf, "compressor-knee",[[items objectAtIndex:7] floatValue]);
336 config_PutFloat(p_intf, "compressor-makeup-gain",[[items objectAtIndex:8] floatValue]);
337 config_PutFloat(p_intf, "spatializer-roomsize",[[items objectAtIndex:9] floatValue]);
338 config_PutFloat(p_intf, "spatializer-width",[[items objectAtIndex:10] floatValue]);
339 config_PutFloat(p_intf, "spatializer-wet",[[items objectAtIndex:11] floatValue]);
340 config_PutFloat(p_intf, "spatializer-dry",[[items objectAtIndex:12] floatValue]);
341 config_PutFloat(p_intf, "spatializer-damp",[[items objectAtIndex:13] floatValue]);
342 config_PutFloat(p_intf, "norm-max-level",[[items objectAtIndex:14] floatValue]);
343 config_PutInt(p_intf, "equalizer-2pass",[[items objectAtIndex:15] intValue]);
345 /* set values on-the-fly if we have an aout */
347 var_SetFloat(p_aout, "compressor-rms-peak", [[items objectAtIndex:2] floatValue]);
348 var_SetFloat(p_aout, "compressor-attack", [[items objectAtIndex:3] floatValue]);
349 var_SetFloat(p_aout, "compressor-release", [[items objectAtIndex:4] floatValue]);
350 var_SetFloat(p_aout, "compressor-threshold", [[items objectAtIndex:5] floatValue]);
351 var_SetFloat(p_aout, "compressor-ratio", [[items objectAtIndex:6] floatValue]);
352 var_SetFloat(p_aout, "compressor-knee", [[items objectAtIndex:7] floatValue]);
353 var_SetFloat(p_aout, "compressor-makeup-gain", [[items objectAtIndex:8] floatValue]);
354 var_SetFloat(p_aout, "spatializer-roomsize", [[items objectAtIndex:9] floatValue]);
355 var_SetFloat(p_aout, "spatializer-width", [[items objectAtIndex:10] floatValue]);
356 var_SetFloat(p_aout, "spatializer-wet", [[items objectAtIndex:11] floatValue]);
357 var_SetFloat(p_aout, "spatializer-dry", [[items objectAtIndex:12] floatValue]);
358 var_SetFloat(p_aout, "spatializer-damp", [[items objectAtIndex:13] floatValue]);
359 var_SetFloat(p_aout, "norm-max-level", [[items objectAtIndex:14] floatValue]);
360 var_SetBool(p_aout, "equalizer-2pass", (BOOL)[[items objectAtIndex:15] intValue]);
364 if ([tempString rangeOfString:@"equalizer"].location == NSNotFound)
365 [o_eq_enable_ckb setState:NSOffState];
367 [o_eq_enable_ckb setState:NSOnState];
368 [o_eq_twopass_ckb setState:[[items objectAtIndex:15] intValue]];
369 [self resetCompressor];
370 [self resetSpatializer];
371 [self resetAudioFilters];
372 [self updatePresetSelector];
374 /* store current profile selection */
375 [defaults setInteger:selectedProfile forKey:@"AudioEffectSelectedProfile"];
376 [defaults synchronize];
379 vlc_object_release(p_aout);
382 - (IBAction)addAudioEffectsProfile:(id)sender
385 VLCEnterTextPanel *panel = [VLCEnterTextPanel sharedInstance];
386 [panel setTitle: _NS("Duplicate current profile for a new profile")];
387 [panel setSubTitle: _NS("Enter a name for the new profile:")];
388 [panel setCancelButtonLabel: _NS("Cancel")];
389 [panel setOKButtonLabel: _NS("Save")];
390 [panel setTarget:self];
391 b_genericAudioProfileInInteraction = YES;
393 [panel runModalForWindow:o_window];
396 - (IBAction)removeAudioEffectsProfile:(id)sender
399 VLCSelectItemInPopupPanel *panel = [VLCSelectItemInPopupPanel sharedInstance];
400 [panel setTitle:_NS("Remove a preset")];
401 [panel setSubTitle:_NS("Select the preset you would like to remove:")];
402 [panel setOKButtonLabel:_NS("Remove")];
403 [panel setCancelButtonLabel:_NS("Cancel")];
404 [panel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"AudioEffectProfileNames"]];
405 [panel setTarget:self];
406 b_genericAudioProfileInInteraction = YES;
408 [panel runModalForWindow:o_window];
412 #pragma mark Equalizer
413 static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
416 char *psz_parser, *psz_string = NULL;
417 vlc_object_t *p_object = VLC_OBJECT(getAout());
418 if (p_object == NULL)
419 p_object = vlc_object_hold(pl_Get(p_custom_intf));
421 psz_string = config_GetPsz(p_custom_intf, "audio-filter");
424 psz_string = var_GetNonEmptyString(p_object, "audio-filter");
426 vlc_object_release(p_object);
431 psz_parser = strstr(psz_string, psz_name);
441 - (void)setupEqualizer
443 vlc_object_t *p_object = VLC_OBJECT(getAout());
444 if (p_object == NULL)
445 p_object = vlc_object_hold(pl_Get(p_intf));
447 var_Create(p_object, "equalizer-preset", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
449 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
450 NSArray *presets = [defaults objectForKey:@"EQNames"];
451 NSString *currentPreset = [NSString stringWithFormat:@"%s",var_GetNonEmptyString(p_object, "equalizer-preset")];
452 NSInteger currentPresetIndex = 0;
453 if ([currentPreset length] > 0) {
454 currentPresetIndex = [presets indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
455 return [obj isEqualToString:currentPreset];
460 snprintf(psz_bands, sizeof(psz_bands),
461 "%.1f %.1f %.1f %.1f %.1f %.1f %.1f "
463 eqz_preset_10b[currentPresetIndex].f_amp[0],
464 eqz_preset_10b[currentPresetIndex].f_amp[1],
465 eqz_preset_10b[currentPresetIndex].f_amp[2],
466 eqz_preset_10b[currentPresetIndex].f_amp[3],
467 eqz_preset_10b[currentPresetIndex].f_amp[4],
468 eqz_preset_10b[currentPresetIndex].f_amp[5],
469 eqz_preset_10b[currentPresetIndex].f_amp[6],
470 eqz_preset_10b[currentPresetIndex].f_amp[7],
471 eqz_preset_10b[currentPresetIndex].f_amp[8],
472 eqz_preset_10b[currentPresetIndex].f_amp[9]);
474 var_Create(p_object, "equalizer-preamp", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
475 var_Create(p_object, "equalizer-bands", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
476 var_SetFloat(p_object, "equalizer-preamp", eqz_preset_10b[currentPresetIndex].f_preamp);
477 var_SetString(p_object, "equalizer-bands", psz_bands);
478 vlc_object_release(p_object);
480 [self updatePresetSelector];
482 [self equalizerUpdated];
485 - (void)updatePresetSelector
487 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
488 NSArray *presets = [defaults objectForKey:@"EQNames"];
490 [o_eq_presets_popup removeAllItems];
491 [o_eq_presets_popup addItemsWithTitles:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
492 [[o_eq_presets_popup menu] addItem:[NSMenuItem separatorItem]];
493 [o_eq_presets_popup addItemWithTitle:_NS("Add new Preset...")];
494 [[o_eq_presets_popup lastItem] setTarget: self];
495 [[o_eq_presets_popup lastItem] setAction: @selector(addPresetAction:)];
497 if ([presets count] > 1) {
498 [o_eq_presets_popup addItemWithTitle:_NS("Organize Presets...")];
499 [[o_eq_presets_popup lastItem] setTarget: self];
500 [[o_eq_presets_popup lastItem] setAction: @selector(deletePresetAction:)];
503 vlc_object_t *p_object = VLC_OBJECT(getAout());
504 if (p_object == NULL)
505 p_object = vlc_object_hold(pl_Get(p_intf));
507 NSString *currentPreset = [NSString stringWithFormat:@"%s",var_GetNonEmptyString(p_object, "equalizer-preset")];
508 vlc_object_release(p_object);
510 NSUInteger currentPresetIndex = 0;
511 if ([currentPreset length] > 0) {
512 currentPresetIndex = [presets indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
513 return [obj isEqualToString:currentPreset];
516 if (currentPresetIndex == NSNotFound)
517 currentPresetIndex = [presets count] - 1;
520 [o_eq_presets_popup selectItemAtIndex:currentPresetIndex];
521 [o_eq_preamp_sld setFloatValue:[[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:currentPresetIndex] floatValue]];
522 [self setBandSliderValuesForPreset:currentPresetIndex];
525 - (void)equalizerUpdated
527 float f_preamp, f_band[10];
528 char *psz_bands, *psz_bands_init, *p_next;
530 bool b_enabled = GetEqualizerStatus(p_intf, (char *)"equalizer");
532 vlc_object_t *p_object = VLC_OBJECT(getAout());
533 if (p_object == NULL)
534 p_object = vlc_object_hold(pl_Get(p_intf));
536 var_Create(p_object, "equalizer-preamp", VLC_VAR_FLOAT |
538 var_Create(p_object, "equalizer-bands", VLC_VAR_STRING |
541 psz_bands = var_GetNonEmptyString(p_object, "equalizer-bands");
543 if (psz_bands == NULL)
544 psz_bands = strdup("0 0 0 0 0 0 0 0 0 0");
546 b_2p = (BOOL)config_GetInt(p_object, "equalizer-2pass");
547 f_preamp = config_GetFloat(p_object, "equalizer-preamp");
549 vlc_object_release(p_object);
552 [self updatePresetSelector];
554 /* Set the the checkboxes */
555 [o_eq_enable_ckb setState: b_enabled];
556 [o_eq_twopass_ckb setState: b_2p];
559 - (id)sliderByIndex:(int)index
562 case 0 : return o_eq_band1_sld;
563 case 1 : return o_eq_band2_sld;
564 case 2 : return o_eq_band3_sld;
565 case 3 : return o_eq_band4_sld;
566 case 4 : return o_eq_band5_sld;
567 case 5 : return o_eq_band6_sld;
568 case 6 : return o_eq_band7_sld;
569 case 7 : return o_eq_band8_sld;
570 case 8 : return o_eq_band9_sld;
571 case 9 : return o_eq_band10_sld;
572 default : return nil;
576 - (void)setBandSlidersValues:(float *)values
578 for (int i = 0 ; i<= 9 ; i++)
579 [self setValue:values[i] forSlider:i];
582 - (void)setBandSliderValuesForPreset:(NSInteger)presetID
584 NSString *preset = [[[NSUserDefaults standardUserDefaults] objectForKey:@"EQValues"] objectAtIndex:presetID];
585 NSArray *values = [preset componentsSeparatedByString:@" "];
586 NSUInteger count = [values count];
587 for (NSUInteger x = 0; x < count; x++)
588 [self setValue:[[values objectAtIndex:x] floatValue] forSlider:x];
591 - (void)initBandSliders
593 for (int i = 0 ; i< 9 ; i++)
594 [self setValue:0.0 forSlider:i];
597 - (NSString *)generatePresetString
599 return [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
600 [o_eq_band1_sld floatValue],
601 [o_eq_band2_sld floatValue],
602 [o_eq_band3_sld floatValue],
603 [o_eq_band4_sld floatValue],
604 [o_eq_band5_sld floatValue],
605 [o_eq_band6_sld floatValue],
606 [o_eq_band7_sld floatValue],
607 [o_eq_band8_sld floatValue],
608 [o_eq_band9_sld floatValue],
609 [o_eq_band10_sld floatValue]];
612 - (void)setValue:(float)value forSlider:(int)index
614 id slider = [self sliderByIndex:index];
617 [slider setFloatValue:value];
620 - (IBAction)eq_enable:(id)sender
622 [self setAudioFilter: "equalizer" on:[sender state]];
625 - (IBAction)eq_bandSliderUpdated:(id)sender
627 vlc_object_t *p_object = VLC_OBJECT(getAout());
628 if (p_object == NULL)
629 p_object = vlc_object_hold(pl_Get(p_intf));
631 var_SetString(p_object, "equalizer-bands", [[self generatePresetString] UTF8String]);
633 /* save changed to config */
634 config_PutPsz(p_intf, "equalizer-bands", [[self generatePresetString] UTF8String]);
636 vlc_object_release(p_object);
639 - (IBAction)eq_changePreset:(id)sender
641 vlc_object_t *p_object = VLC_OBJECT(getAout());
642 if (p_object == NULL)
643 p_object = vlc_object_hold(pl_Get(p_intf));
645 NSInteger numberOfChosenPreset = [sender indexOfSelectedItem];
646 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
648 NSString *preset = [[defaults objectForKey:@"EQValues"] objectAtIndex:numberOfChosenPreset];
649 NSString *preamp = [[defaults objectForKey:@"EQPreampValues"] objectAtIndex:numberOfChosenPreset];
651 var_SetString(p_object, "equalizer-bands", [preset UTF8String]);
652 var_SetFloat(p_object, "equalizer-preamp", [preamp floatValue]);
653 var_SetString(p_object , "equalizer-preset" , [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String]);
655 [o_eq_preamp_sld setFloatValue: [preamp floatValue]];
656 [self setBandSliderValuesForPreset:numberOfChosenPreset];
658 /* save changed to config */
659 config_PutPsz(p_intf, "equalizer-bands", [preset UTF8String]);
660 config_PutFloat(p_intf, "equalizer-preamp", [preamp floatValue]);
661 config_PutPsz(p_intf, "equalizer-preset", [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String]);
663 vlc_object_release(p_object);
666 - (IBAction)eq_preampSliderUpdated:(id)sender
668 float f_preamp = [sender floatValue] ;
670 vlc_object_t *p_object = VLC_OBJECT(getAout());
671 if (p_object == NULL)
672 p_object = vlc_object_hold(pl_Get(p_intf));
674 var_SetFloat(p_object, "equalizer-preamp", f_preamp);
676 /* save changed to config */
677 config_PutFloat(p_intf, "equalizer-preamp", f_preamp);
679 vlc_object_release(p_object);
681 - (IBAction)eq_twopass:(id)sender
683 bool b_2p = [sender state] ? true : false;
685 vlc_object_t *p_object = VLC_OBJECT(getAout());
686 if (p_object == NULL)
687 p_object = vlc_object_hold(pl_Get(p_intf));
689 var_SetBool(p_object, "equalizer-2pass", b_2p);
691 /* save changed to config */
692 config_PutInt(p_intf, "equalizer-2pass", (int)b_2p);
694 vlc_object_release(p_object);
697 - (IBAction)addPresetAction:(id)sender
700 VLCEnterTextPanel *panel = [VLCEnterTextPanel sharedInstance];
701 [panel setTitle: _NS("Save current selection as new preset")];
702 [panel setSubTitle: _NS("Enter a name for the new preset:")];
703 [panel setCancelButtonLabel: _NS("Cancel")];
704 [panel setOKButtonLabel: _NS("Save")];
705 [panel setTarget:self];
706 b_genericAudioProfileInInteraction = NO;
708 [panel runModalForWindow:o_window];
711 - (void)panel:(VLCEnterTextPanel *)panel returnValue:(NSUInteger)value text:(NSString *)text
714 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
717 if (!b_genericAudioProfileInInteraction) {
718 if (value == NSOKButton && [text length] > 0) {
719 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
720 [workArray addObject:[self generatePresetString]];
721 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
723 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
724 [workArray addObject:text];
725 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
727 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
728 [workArray addObject:[NSString stringWithFormat:@"%.1f", [o_eq_preamp_sld floatValue]]];
729 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
731 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
732 [workArray addObject:[text decomposedStringWithCanonicalMapping]];
733 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
735 [defaults synchronize];
737 /* update VLC internals */
738 vlc_object_t *p_object = VLC_OBJECT(getAout());
739 if (p_object == NULL)
740 p_object = vlc_object_hold(pl_Get(p_intf));
742 var_SetString(p_object, "equalizer-preset", [[text decomposedStringWithCanonicalMapping] UTF8String]);
743 config_PutPsz(p_object, "equalizer-preset", [[text decomposedStringWithCanonicalMapping] UTF8String]);
745 vlc_object_release(p_object);
748 [self updatePresetSelector];
754 if (value != NSOKButton) {
755 [o_profile_pop selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
759 NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
761 // duplicate names are not allowed in the popup control
762 if ([text length] == 0 || [profileNames containsObject:text]) {
763 [o_profile_pop selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
765 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
766 [alert setAlertStyle:NSCriticalAlertStyle];
767 [alert setMessageText:_NS("Please enter a unique name for the new profile.")];
768 [alert setInformativeText:_NS("Multiple profiles with the same name are not allowed.")];
770 [alert beginSheetModalForWindow:o_window
777 NSString *newProfile = [self generateProfileString];
779 /* add string to user defaults as well as a label */
780 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
781 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
782 [workArray addObject:newProfile];
783 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
784 [defaults setInteger:[workArray count] - 1 forKey:@"AudioEffectSelectedProfile"];
786 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
787 [workArray addObject:text];
788 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
792 [defaults synchronize];
793 [self resetProfileSelector];
797 - (IBAction)deletePresetAction:(id)sender
799 VLCSelectItemInPopupPanel *panel = [VLCSelectItemInPopupPanel sharedInstance];
800 [panel setTitle:_NS("Remove a preset")];
801 [panel setSubTitle:_NS("Select the preset you would like to remove:")];
802 [panel setOKButtonLabel:_NS("Remove")];
803 [panel setCancelButtonLabel:_NS("Cancel")];
804 [panel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
805 [panel setTarget:self];
806 b_genericAudioProfileInInteraction = NO;
808 [panel runModalForWindow:o_window];
811 - (void)panel:(VLCSelectItemInPopupPanel *)panel returnValue:(NSUInteger)value item:(NSUInteger)item
813 if (value == NSOKButton) {
814 if (!b_genericAudioProfileInInteraction) {
815 /* remove requested profile from the arrays */
816 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
817 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
818 [workArray removeObjectAtIndex:item];
819 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
821 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
822 [workArray removeObjectAtIndex:item];
823 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
825 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
826 [workArray removeObjectAtIndex:item];
827 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
829 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
830 [workArray removeObjectAtIndex:item];
831 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
833 [defaults synchronize];
836 [self updatePresetSelector];
838 /* remove selected profile from settings */
839 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
840 NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
841 [workArray removeObjectAtIndex:item];
842 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
844 workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
845 [workArray removeObjectAtIndex:item];
846 [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
849 if (i_old_profile_index >= item)
850 [defaults setInteger:i_old_profile_index - 1 forKey:@"AudioEffectSelectedProfile"];
853 [defaults synchronize];
854 [self resetProfileSelector];
860 #pragma mark Compressor
861 - (void)resetCompressor
864 psz_afilters = config_GetPsz(p_intf, "audio-filter");
866 [o_comp_enable_ckb setState: (NSInteger)strstr(psz_afilters, "compressor") ];
870 [o_comp_enable_ckb setState: NSOffState];
872 [o_comp_band1_sld setFloatValue: config_GetFloat(p_intf, "compressor-rms-peak")];
873 [o_comp_band1_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [o_comp_band1_sld floatValue]]];
874 [o_comp_band2_sld setFloatValue: config_GetFloat(p_intf, "compressor-attack")];
875 [o_comp_band2_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [o_comp_band2_sld floatValue]]];
876 [o_comp_band3_sld setFloatValue: config_GetFloat(p_intf, "compressor-release")];
877 [o_comp_band3_fld setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [o_comp_band3_sld floatValue]]];
878 [o_comp_band4_sld setFloatValue: config_GetFloat(p_intf, "compressor-threshold")];
879 [o_comp_band4_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [o_comp_band4_sld floatValue]]];
880 [o_comp_band5_sld setFloatValue: config_GetFloat(p_intf, "compressor-ratio")];
881 [o_comp_band5_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [o_comp_band5_sld floatValue]]];
882 [o_comp_band6_sld setFloatValue: config_GetFloat(p_intf, "compressor-knee")];
883 [o_comp_band6_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [o_comp_band6_sld floatValue]]];
884 [o_comp_band7_sld setFloatValue: config_GetFloat(p_intf, "compressor-makeup-gain")];
885 [o_comp_band7_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [o_comp_band7_sld floatValue]]];
888 - (IBAction)resetCompressorValues:(id)sender
890 config_PutFloat(p_intf, "compressor-rms-peak", 0.000000);
891 config_PutFloat(p_intf, "compressor-attack", 25.000000);
892 config_PutFloat(p_intf, "compressor-release", 100.000000);
893 config_PutFloat(p_intf, "compressor-threshold", -11.000000);
894 config_PutFloat(p_intf, "compressor-ratio", 8.000000);
895 config_PutFloat(p_intf, "compressor-knee", 2.500000);
896 config_PutFloat(p_intf, "compressor-makeup-gain", 7.000000);
898 audio_output_t *p_aout = getAout();
900 var_SetFloat(p_aout, "compressor-rms-peak", 0.000000);
901 var_SetFloat(p_aout, "compressor-attack", 25.000000);
902 var_SetFloat(p_aout, "compressor-release", 100.000000);
903 var_SetFloat(p_aout, "compressor-threshold", -11.000000);
904 var_SetFloat(p_aout, "compressor-ratio", 8.000000);
905 var_SetFloat(p_aout, "compressor-knee", 2.500000);
906 var_SetFloat(p_aout, "compressor-makeup-gain", 7.000000);
907 vlc_object_release(p_aout);
909 [self resetCompressor];
912 - (IBAction)comp_enable:(id)sender
914 [self setAudioFilter:"compressor" on:[sender state]];
917 - (IBAction)comp_sliderUpdated:(id)sender
919 audio_output_t *p_aout = getAout();
921 if (sender == o_comp_band1_sld)
922 value = "compressor-rms-peak";
923 else if (sender == o_comp_band2_sld)
924 value = "compressor-attack";
925 else if (sender == o_comp_band3_sld)
926 value = "compressor-release";
927 else if (sender == o_comp_band4_sld)
928 value = "compressor-threshold";
929 else if (sender == o_comp_band5_sld)
930 value = "compressor-ratio";
931 else if (sender == o_comp_band6_sld)
932 value = "compressor-knee";
933 else if (sender == o_comp_band7_sld)
934 value = "compressor-makeup-gain";
937 var_SetFloat(p_aout, value, [sender floatValue]);
938 vlc_object_release(p_aout);
940 config_PutFloat(p_intf, value, [sender floatValue]);
942 if (sender == o_comp_band1_sld)
943 [o_comp_band1_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
944 else if (sender == o_comp_band2_sld)
945 [o_comp_band2_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [sender floatValue]]];
946 else if (sender == o_comp_band3_sld)
947 [o_comp_band3_fld setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [sender floatValue]]];
948 else if (sender == o_comp_band4_sld)
949 [o_comp_band4_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [sender floatValue]]];
950 else if (sender == o_comp_band5_sld)
951 [o_comp_band5_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [sender floatValue]]];
952 else if (sender == o_comp_band6_sld)
953 [o_comp_band6_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [sender floatValue]]];
954 else if (sender == o_comp_band7_sld)
955 [o_comp_band7_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [sender floatValue]]];
959 #pragma mark Spatializer
960 - (void)resetSpatializer
963 psz_afilters = config_GetPsz(p_intf, "audio-filter");
965 [o_spat_enable_ckb setState: (NSInteger)strstr(psz_afilters, "spatializer") ];
969 [o_spat_enable_ckb setState: NSOffState];
971 #define setSlider(bandsld, bandfld, var) \
972 [bandsld setFloatValue: config_GetFloat(p_intf, var) * 10.]; \
973 [bandfld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [bandsld floatValue]]]
975 setSlider(o_spat_band1_sld, o_spat_band1_fld, "spatializer-roomsize");
976 setSlider(o_spat_band2_sld, o_spat_band2_fld, "spatializer-width");
977 setSlider(o_spat_band3_sld, o_spat_band3_fld, "spatializer-wet");
978 setSlider(o_spat_band4_sld, o_spat_band4_fld, "spatializer-dry");
979 setSlider(o_spat_band5_sld, o_spat_band5_fld, "spatializer-damp");
984 - (IBAction)resetSpatializerValues:(id)sender
986 config_PutFloat(p_intf, "spatializer-roomsize", .85);
987 config_PutFloat(p_intf, "spatializer-width", 1.);
988 config_PutFloat(p_intf, "spatializer-wet", .4);
989 config_PutFloat(p_intf, "spatializer-dry", .5);
990 config_PutFloat(p_intf, "spatializer-damp", .5);
992 audio_output_t *p_aout = getAout();
994 var_SetFloat(p_aout, "spatializer-roomsize", .85);
995 var_SetFloat(p_aout, "spatializer-width", 1.);
996 var_SetFloat(p_aout, "spatializer-wet", .4);
997 var_SetFloat(p_aout, "spatializer-dry", .5);
998 var_SetFloat(p_aout, "spatializer-damp", .5);
999 vlc_object_release(p_aout);
1001 [self resetSpatializer];
1004 - (IBAction)spat_enable:(id)sender
1006 [self setAudioFilter:"spatializer" on:[sender state]];
1009 - (IBAction)spat_sliderUpdated:(id)sender
1011 audio_output_t *p_aout = getAout();
1013 if (sender == o_spat_band1_sld)
1014 value = "spatializer-roomsize";
1015 else if (sender == o_spat_band2_sld)
1016 value = "spatializer-width";
1017 else if (sender == o_spat_band3_sld)
1018 value = "spatializer-wet";
1019 else if (sender == o_spat_band4_sld)
1020 value = "spatializer-dry";
1021 else if (sender == o_spat_band5_sld)
1022 value = "spatializer-damp";
1025 var_SetFloat(p_aout, value, [sender floatValue] / 10.);
1026 vlc_object_release(p_aout);
1028 config_PutFloat(p_intf, value, [sender floatValue] / 10.);
1030 if (sender == o_spat_band1_sld)
1031 [o_spat_band1_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
1032 else if (sender == o_spat_band2_sld)
1033 [o_spat_band2_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
1034 else if (sender == o_spat_band3_sld)
1035 [o_spat_band3_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
1036 else if (sender == o_spat_band4_sld)
1037 [o_spat_band4_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
1038 else if (sender == o_spat_band5_sld)
1039 [o_spat_band5_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
1044 - (void)resetAudioFilters
1047 psz_afilters = config_GetPsz(p_intf, "audio-filter");
1049 [o_filter_headPhone_ckb setState: (NSInteger)strstr(psz_afilters, "headphone") ];
1050 [o_filter_normLevel_ckb setState: (NSInteger)strstr(psz_afilters, "normvol") ];
1051 [o_filter_karaoke_ckb setState: (NSInteger)strstr(psz_afilters, "karaoke") ];
1054 [o_filter_headPhone_ckb setState: NSOffState];
1055 [o_filter_normLevel_ckb setState: NSOffState];
1056 [o_filter_karaoke_ckb setState: NSOffState];
1058 [o_filter_normLevel_sld setFloatValue: config_GetFloat(p_intf, "norm-max-level")];
1061 - (IBAction)filter_enableHeadPhoneVirt:(id)sender
1063 [self setAudioFilter: "headphone" on:[sender state]];
1066 - (IBAction)filter_enableVolumeNorm:(id)sender
1068 [self setAudioFilter: "normvol" on:[sender state]];
1071 - (IBAction)filter_volNormSliderUpdated:(id)sender
1073 audio_output_t *p_aout = getAout();
1076 var_SetFloat(p_aout, "norm-max-level", [o_filter_normLevel_sld floatValue]);
1077 vlc_object_release(p_aout);
1080 config_PutFloat(p_intf, "norm-max-level", [o_filter_normLevel_sld floatValue]);
1083 - (IBAction)filter_enableKaraoke:(id)sender
1085 [self setAudioFilter: "karaoke" on:[sender state]];