]> git.sesse.net Git - vlc/blobdiff - modules/gui/macosx/AudioEffects.m
macosx: hide bottom bar of main window only when really necessary
[vlc] / modules / gui / macosx / AudioEffects.m
index 3c6c4564ba6e36c621b12ef3ab02abb37ad2c656..38a32c9c730811a6075d7a00f78a5bf7962a79bd 100644 (file)
 #import "SharedDialogs.h"
 
 #import <vlc_common.h>
-#import <vlc_aout_intf.h>
+#import <vlc_strings.h>
 
 #import <math.h>
 
+@interface VLCAudioEffects (Internal)
+- (void)resetProfileSelector;
+- (void)updatePresetSelector;
+- (void)setBandSliderValuesForPreset:(NSInteger)presetID;
+@end
+
 #pragma mark -
-#pragma mark Initialization & Generic code
+#pragma mark Initialization
 
 @implementation VLCAudioEffects
 static VLCAudioEffects *_o_sharedInstance = nil;
@@ -51,11 +57,11 @@ static VLCAudioEffects *_o_sharedInstance = nil;
 + (void)initialize{
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 
-    NSString * workString;
-    NSMutableArray * workValues = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
-    NSMutableArray * workPreamp = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
-    NSMutableArray * workTitles = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
-    NSMutableArray * workNames = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
+    NSString *workString;
+    NSMutableArray *workValues = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
+    NSMutableArray *workPreamp = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
+    NSMutableArray *workTitles = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
+    NSMutableArray *workNames = [[NSMutableArray alloc] initWithCapacity:NB_PRESETS];
 
     for (int i = 0 ; i < NB_PRESETS ; i++) {
         workString = [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
@@ -75,7 +81,10 @@ static VLCAudioEffects *_o_sharedInstance = nil;
         [workNames addObject:[NSString stringWithUTF8String:preset_list[i]]];
     }
 
-    NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithArray:workValues], @"EQValues", [NSArray arrayWithArray:workPreamp], @"EQPreampValues", [NSArray arrayWithArray:workTitles], @"EQTitles", [NSArray arrayWithArray:workNames], @"EQNames", nil];
+    NSString *defaultProfile = [NSString stringWithFormat:@"ZmxhdA==;;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%i",
+                                .0,25.,100.,-11.,8.,2.5,7.,.85,1.,.4,.5,.5,2.,0];
+
+    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];
     [defaults registerDefaults:appDefaults];
 
     [workValues release];
@@ -90,6 +99,7 @@ static VLCAudioEffects *_o_sharedInstance = nil;
         [self dealloc];
     else {
         p_intf = VLCIntf;
+        i_old_profile_index = -1;
         _o_sharedInstance = [super init];
     }
 
@@ -128,6 +138,7 @@ static VLCAudioEffects *_o_sharedInstance = nil;
     [o_filter_headPhone_ckb setTitle:_NS("Headphone virtualization")];
     [o_filter_normLevel_ckb setTitle:_NS("Volume normalization")];
     [o_filter_normLevel_lbl setStringValue:_NS("Maximum level")];
+    [o_filter_karaoke_ckb setTitle:_NS("Karaoke")];
 
     /* generic */
     [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"equalizer"]] setLabel:_NS("Equalizer")];
@@ -143,20 +154,16 @@ static VLCAudioEffects *_o_sharedInstance = nil;
     [self resetCompressor];
     [self resetSpatializer];
     [self resetAudioFilters];
+    [self resetProfileSelector];
 }
 
-- (IBAction)toggleWindow:(id)sender
-{
-    if ([o_window isVisible])
-        [o_window orderOut:sender];
-    else
-        [o_window makeKeyAndOrderFront:sender];
-}
+#pragma mark -
+#pragma mark internal functions
 
 - (void)setAudioFilter: (char *)psz_name on:(BOOL)b_on
 {
     char *psz_tmp;
-    audio_output_t * p_aout = getAout();
+    audio_output_t *p_aout = getAout();
     if (p_aout)
         psz_tmp = var_GetNonEmptyString(p_aout, "audio-filter");
     else
@@ -165,7 +172,7 @@ static VLCAudioEffects *_o_sharedInstance = nil;
     if (b_on) {
         if (!psz_tmp)
             config_PutPsz(p_intf, "audio-filter", psz_name);
-        else if ((NSInteger)strstr(psz_tmp, psz_name) == NO) {
+        else if (strstr(psz_tmp, psz_name) == NULL) {
             psz_tmp = (char *)[[NSString stringWithFormat: @"%s:%s", psz_tmp, psz_name] UTF8String];
             config_PutPsz(p_intf, "audio-filter", psz_tmp);
         }
@@ -179,15 +186,232 @@ static VLCAudioEffects *_o_sharedInstance = nil;
     }
 
     if (p_aout) {
-        aout_EnableFilter(pl_Get(p_intf), psz_name, b_on);
+        playlist_EnableAudioFilter(pl_Get(p_intf), psz_name, b_on);
         vlc_object_release(p_aout);
     }
 }
 
+- (void)resetProfileSelector
+{
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    [o_profile_pop removeAllItems];
+
+    NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
+    [o_profile_pop addItemsWithTitles:profileNames];
+
+    [[o_profile_pop menu] addItem:[NSMenuItem separatorItem]];
+    [o_profile_pop addItemWithTitle:_NS("Duplicate current profile...")];
+    [[o_profile_pop lastItem] setTarget: self];
+    [[o_profile_pop lastItem] setAction: @selector(addAudioEffectsProfile:)];
+
+    if ([profileNames count] > 1) {
+        [o_profile_pop addItemWithTitle:_NS("Organize Profiles...")];
+        [[o_profile_pop lastItem] setTarget: self];
+        [[o_profile_pop lastItem] setAction: @selector(removeAudioEffectsProfile:)];
+    }
+
+    [o_profile_pop selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
+    [self profileSelectorAction:self];
+}
+
+#pragma mark -
+#pragma mark generic code
+- (void)updateCocoaWindowLevel:(NSInteger)i_level
+{
+    if (o_window && [o_window isVisible] && [o_window level] != i_level)
+        [o_window setLevel: i_level];
+}
+
+- (IBAction)toggleWindow:(id)sender
+{
+    if ([o_window isVisible])
+        [o_window orderOut:sender];
+    else {
+        [o_window setLevel: [[[VLCMain sharedInstance] voutController] currentWindowLevel]];
+        [o_window makeKeyAndOrderFront:sender];
+    }
+}
+
+- (NSString *)generateProfileString
+{
+    vlc_object_t *p_object = VLC_OBJECT(getAout());
+    if (p_object == NULL)
+        p_object = vlc_object_hold(pl_Get(p_intf));
+
+    NSString *o_str = [NSString stringWithFormat:@"%s;%s;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%f;%lli",
+                       vlc_b64_encode(var_GetNonEmptyString(p_object, "equalizer-preset")),
+                       vlc_b64_encode(config_GetPsz(p_intf, "audio-filter")),
+                       config_GetFloat(p_intf, "compressor-rms-peak"),
+                       config_GetFloat(p_intf, "compressor-attack"),
+                       config_GetFloat(p_intf, "compressor-release"),
+                       config_GetFloat(p_intf, "compressor-threshold"),
+                       config_GetFloat(p_intf, "compressor-ratio"),
+                       config_GetFloat(p_intf, "compressor-knee"),
+                       config_GetFloat(p_intf, "compressor-makeup-gain"),
+                       config_GetFloat(p_intf, "spatializer-roomsize"),
+                       config_GetFloat(p_intf, "spatializer-width"),
+                       config_GetFloat(p_intf, "spatializer-wet"),
+                       config_GetFloat(p_intf, "spatializer-dry"),
+                       config_GetFloat(p_intf, "spatializer-damp"),
+                       config_GetFloat(p_intf, "norm-max-level"),
+                       config_GetInt(p_intf,"equalizer-2pass")];
+
+    vlc_object_release(p_object);
+    return o_str;
+}
+
+- (void)saveCurrentProfile
+{
+    if (i_old_profile_index == -1)
+        return;
+
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    /* fetch all the current settings in a uniform string */
+    NSString *newProfile = [self generateProfileString];
+
+    NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
+    if (i_old_profile_index >= [workArray count])
+        return;
+
+    [workArray replaceObjectAtIndex:i_old_profile_index withObject:newProfile];
+    [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
+    [workArray release];
+    [defaults synchronize];
+}
+
+- (IBAction)profileSelectorAction:(id)sender
+{
+    [self saveCurrentProfile];
+    i_old_profile_index = [o_profile_pop indexOfSelectedItem];
+
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    NSUInteger selectedProfile = [o_profile_pop indexOfSelectedItem];
+
+    audio_output_t *p_aout = getAout();
+    playlist_t *p_playlist = pl_Get(p_intf);
+
+    if (p_aout) {
+        /* disable existing filters */
+        playlist_EnableAudioFilter(p_playlist, "equalizer", false);
+        playlist_EnableAudioFilter(p_playlist, "compressor", false);
+        playlist_EnableAudioFilter(p_playlist, "spatializer", false);
+        playlist_EnableAudioFilter(p_playlist, "compressor", false);
+        playlist_EnableAudioFilter(p_playlist, "headphone", false);
+        playlist_EnableAudioFilter(p_playlist, "normvol", false);
+        playlist_EnableAudioFilter(p_playlist, "karaoke", false);
+    }
+
+    /* fetch preset */
+    NSArray *items = [[[defaults objectForKey:@"AudioEffectProfiles"] objectAtIndex:selectedProfile] componentsSeparatedByString:@";"];
+
+    /* eq preset */
+    vlc_object_t *p_object = VLC_OBJECT(getAout());
+    if (p_object == NULL)
+        p_object = vlc_object_hold(pl_Get(p_intf));
+    var_SetString(p_object,"equalizer-preset",vlc_b64_decode([[items objectAtIndex:0] UTF8String]));
+    vlc_object_release(p_object);
+
+    /* filter handling */
+    NSString *tempString = [NSString stringWithFormat:@"%s", vlc_b64_decode([[items objectAtIndex:1] UTF8String])];
+    NSArray *tempArray;
+    NSUInteger count;
+    /* enable the new filters, if we have an aout */
+    if (p_aout) {
+        if ([tempString length] > 0) {
+            tempArray = [tempString componentsSeparatedByString:@":"];
+            count = [tempArray count];
+            for (NSUInteger x = 0; x < count; x++)
+                playlist_EnableAudioFilter(p_playlist, (char *)[[tempArray objectAtIndex:x] UTF8String], true);
+        }
+    }
+    config_PutPsz(p_intf,"audio-filter",[tempString UTF8String]);
+
+    /* values */
+    config_PutFloat(p_intf, "compressor-rms-peak",[[items objectAtIndex:2] floatValue]);
+    config_PutFloat(p_intf, "compressor-attack",[[items objectAtIndex:3] floatValue]);
+    config_PutFloat(p_intf, "compressor-release",[[items objectAtIndex:4] floatValue]);
+    config_PutFloat(p_intf, "compressor-threshold",[[items objectAtIndex:5] floatValue]);
+    config_PutFloat(p_intf, "compressor-ratio",[[items objectAtIndex:6] floatValue]);
+    config_PutFloat(p_intf, "compressor-knee",[[items objectAtIndex:7] floatValue]);
+    config_PutFloat(p_intf, "compressor-makeup-gain",[[items objectAtIndex:8] floatValue]);
+    config_PutFloat(p_intf, "spatializer-roomsize",[[items objectAtIndex:9] floatValue]);
+    config_PutFloat(p_intf, "spatializer-width",[[items objectAtIndex:10] floatValue]);
+    config_PutFloat(p_intf, "spatializer-wet",[[items objectAtIndex:11] floatValue]);
+    config_PutFloat(p_intf, "spatializer-dry",[[items objectAtIndex:12] floatValue]);
+    config_PutFloat(p_intf, "spatializer-damp",[[items objectAtIndex:13] floatValue]);
+    config_PutFloat(p_intf, "norm-max-level",[[items objectAtIndex:14] floatValue]);
+    config_PutInt(p_intf, "equalizer-2pass",[[items objectAtIndex:15] intValue]);
+
+    /* set values on-the-fly if we have an aout */
+    if (p_aout) {
+        var_SetFloat(p_aout, "compressor-rms-peak", [[items objectAtIndex:2] floatValue]);
+        var_SetFloat(p_aout, "compressor-attack", [[items objectAtIndex:3] floatValue]);
+        var_SetFloat(p_aout, "compressor-release", [[items objectAtIndex:4] floatValue]);
+        var_SetFloat(p_aout, "compressor-threshold", [[items objectAtIndex:5] floatValue]);
+        var_SetFloat(p_aout, "compressor-ratio", [[items objectAtIndex:6] floatValue]);
+        var_SetFloat(p_aout, "compressor-knee", [[items objectAtIndex:7] floatValue]);
+        var_SetFloat(p_aout, "compressor-makeup-gain", [[items objectAtIndex:8] floatValue]);
+        var_SetFloat(p_aout, "spatializer-roomsize", [[items objectAtIndex:9] floatValue]);
+        var_SetFloat(p_aout, "spatializer-width", [[items objectAtIndex:10] floatValue]);
+        var_SetFloat(p_aout, "spatializer-wet", [[items objectAtIndex:11] floatValue]);
+        var_SetFloat(p_aout, "spatializer-dry", [[items objectAtIndex:12] floatValue]);
+        var_SetFloat(p_aout, "spatializer-damp", [[items objectAtIndex:13] floatValue]);
+        var_SetFloat(p_aout, "norm-max-level", [[items objectAtIndex:14] floatValue]);
+        var_SetBool(p_aout, "equalizer-2pass", (BOOL)[[items objectAtIndex:15] intValue]);
+    }
+
+    /* update UI */
+    if ([tempString rangeOfString:@"equalizer"].location == NSNotFound)
+        [o_eq_enable_ckb setState:NSOffState];
+    else
+        [o_eq_enable_ckb setState:NSOnState];
+    [o_eq_twopass_ckb setState:[[items objectAtIndex:15] intValue]];
+    [self resetCompressor];
+    [self resetSpatializer];
+    [self resetAudioFilters];
+    [self updatePresetSelector];
+
+    /* store current profile selection */
+    [defaults setInteger:selectedProfile forKey:@"AudioEffectSelectedProfile"];
+    [defaults synchronize];
+
+    if (p_aout)
+        vlc_object_release(p_aout);
+}
+
+- (IBAction)addAudioEffectsProfile:(id)sender
+{
+    /* show panel */
+    VLCEnterTextPanel *panel = [VLCEnterTextPanel sharedInstance];
+    [panel setTitle: _NS("Duplicate current profile for a new profile")];
+    [panel setSubTitle: _NS("Enter a name for the new profile:")];
+    [panel setCancelButtonLabel: _NS("Cancel")];
+    [panel setOKButtonLabel: _NS("Save")];
+    [panel setTarget:self];
+    b_genericAudioProfileInInteraction = YES;
+
+    [panel runModalForWindow:o_window];
+}
+
+- (IBAction)removeAudioEffectsProfile:(id)sender
+{
+    /* show panel */
+    VLCSelectItemInPopupPanel *panel = [VLCSelectItemInPopupPanel sharedInstance];
+    [panel setTitle:_NS("Remove a preset")];
+    [panel setSubTitle:_NS("Select the preset you would like to remove:")];
+    [panel setOKButtonLabel:_NS("Remove")];
+    [panel setCancelButtonLabel:_NS("Cancel")];
+    [panel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"AudioEffectProfileNames"]];
+    [panel setTarget:self];
+    b_genericAudioProfileInInteraction = YES;
+
+    [panel runModalForWindow:o_window];
+}
+
 #pragma mark -
 #pragma mark Equalizer
 static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
-                             char *psz_name)
+                               char *psz_name)
 {
     char *psz_parser, *psz_string = NULL;
     vlc_object_t *p_object = VLC_OBJECT(getAout());
@@ -222,12 +446,15 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 
     var_Create(p_object, "equalizer-preset", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
 
-    NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
-    NSArray * presets = [defaults objectForKey:@"EQNames"];
-    NSString * currentPreset = [NSString stringWithUTF8String:var_GetNonEmptyString(p_object, "equalizer-preset")];
-    NSInteger currentPresetIndex = [presets indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
-        return [obj isEqualToString:currentPreset];
-    }];
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    NSArray *presets = [defaults objectForKey:@"EQNames"];
+    NSString *currentPreset = [NSString stringWithFormat:@"%s",var_GetNonEmptyString(p_object, "equalizer-preset")];
+    NSInteger currentPresetIndex = 0;
+    if ([currentPreset length] > 0) {
+        currentPresetIndex = [presets indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
+            return [obj isEqualToString:currentPreset];
+        }];
+    }
 
     char psz_bands[100];
     snprintf(psz_bands, sizeof(psz_bands),
@@ -257,32 +484,42 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 
 - (void)updatePresetSelector
 {
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    NSArray *presets = [defaults objectForKey:@"EQNames"];
+
     [o_eq_presets_popup removeAllItems];
     [o_eq_presets_popup addItemsWithTitles:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
     [[o_eq_presets_popup menu] addItem:[NSMenuItem separatorItem]];
     [o_eq_presets_popup addItemWithTitle:_NS("Add new Preset...")];
     [[o_eq_presets_popup lastItem] setTarget: self];
     [[o_eq_presets_popup lastItem] setAction: @selector(addPresetAction:)];
-    [o_eq_presets_popup addItemWithTitle:_NS("Organize Presets...")];
-    [[o_eq_presets_popup lastItem] setTarget: self];
-    [[o_eq_presets_popup lastItem] setAction: @selector(deletePresetAction:)];
+
+    if ([presets count] > 1) {
+        [o_eq_presets_popup addItemWithTitle:_NS("Organize Presets...")];
+        [[o_eq_presets_popup lastItem] setTarget: self];
+        [[o_eq_presets_popup lastItem] setAction: @selector(deletePresetAction:)];
+    }
 
     vlc_object_t *p_object = VLC_OBJECT(getAout());
     if (p_object == NULL)
         p_object = vlc_object_hold(pl_Get(p_intf));
 
-    NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
-    NSArray * presets = [defaults objectForKey:@"EQNames"];
-    NSString * currentPreset = [NSString stringWithUTF8String:var_GetNonEmptyString(p_object, "equalizer-preset")];
-    NSInteger currentPresetIndex = [presets indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
-        return [obj isEqualToString:currentPreset];
-    }];
+    NSString *currentPreset = [NSString stringWithFormat:@"%s",var_GetNonEmptyString(p_object, "equalizer-preset")];
+    vlc_object_release(p_object);
+
+    NSUInteger currentPresetIndex = 0;
+    if ([currentPreset length] > 0) {
+        currentPresetIndex = [presets indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
+            return [obj isEqualToString:currentPreset];
+        }];
+
+        if (currentPresetIndex == NSNotFound)
+            currentPresetIndex = [presets count] - 1;
+    }    
 
     [o_eq_presets_popup selectItemAtIndex:currentPresetIndex];
     [o_eq_preamp_sld setFloatValue:[[[defaults objectForKey:@"EQPreampValues"] objectAtIndex:currentPresetIndex] floatValue]];
     [self setBandSliderValuesForPreset:currentPresetIndex];
-
-    vlc_object_release(p_object);
 }
 
 - (void)equalizerUpdated
@@ -291,8 +528,8 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
     char *psz_bands, *psz_bands_init, *p_next;
     bool b_2p;
     bool b_enabled = GetEqualizerStatus(p_intf, (char *)"equalizer");
-    vlc_object_t *p_object = VLC_OBJECT(getAout());
 
+    vlc_object_t *p_object = VLC_OBJECT(getAout());
     if (p_object == NULL)
         p_object = vlc_object_hold(pl_Get(p_intf));
 
@@ -344,8 +581,8 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 
 - (void)setBandSliderValuesForPreset:(NSInteger)presetID
 {
-    NSString * preset = [[[NSUserDefaults standardUserDefaults] objectForKey:@"EQValues"] objectAtIndex:presetID];
-    NSArray * values = [preset componentsSeparatedByString:@" "];
+    NSString *preset = [[[NSUserDefaults standardUserDefaults] objectForKey:@"EQValues"] objectAtIndex:presetID];
+    NSArray *values = [preset componentsSeparatedByString:@" "];
     NSUInteger count = [values count];
     for (NSUInteger x = 0; x < count; x++)
         [self setValue:[[values objectAtIndex:x] floatValue] forSlider:x];
@@ -388,7 +625,6 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 - (IBAction)eq_bandSliderUpdated:(id)sender
 {
     vlc_object_t *p_object = VLC_OBJECT(getAout());
-
     if (p_object == NULL)
         p_object = vlc_object_hold(pl_Get(p_intf));
 
@@ -402,11 +638,12 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 
 - (IBAction)eq_changePreset:(id)sender
 {
-    vlc_object_t *p_object= VLC_OBJECT(getAout());
+    vlc_object_t *p_object = VLC_OBJECT(getAout());
     if (p_object == NULL)
         p_object = vlc_object_hold(pl_Get(p_intf));
+
     NSInteger numberOfChosenPreset = [sender indexOfSelectedItem];
-    NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 
     NSString *preset = [[defaults objectForKey:@"EQValues"] objectAtIndex:numberOfChosenPreset];
     NSString *preamp = [[defaults objectForKey:@"EQPreampValues"] objectAtIndex:numberOfChosenPreset];
@@ -444,8 +681,8 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 - (IBAction)eq_twopass:(id)sender
 {
     bool b_2p = [sender state] ? true : false;
-    audio_output_t *p_aout = getAout();
-    vlc_object_t *p_object= VLC_OBJECT(p_aout);
+
+    vlc_object_t *p_object = VLC_OBJECT(getAout());
     if (p_object == NULL)
         p_object = vlc_object_hold(pl_Get(p_intf));
 
@@ -460,22 +697,26 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 - (IBAction)addPresetAction:(id)sender
 {
     /* show panel */
-    VLCEnterTextPanel * panel = [VLCEnterTextPanel sharedInstance];
+    VLCEnterTextPanel *panel = [VLCEnterTextPanel sharedInstance];
     [panel setTitle: _NS("Save current selection as new preset")];
     [panel setSubTitle: _NS("Enter a name for the new preset:")];
     [panel setCancelButtonLabel: _NS("Cancel")];
     [panel setOKButtonLabel: _NS("Save")];
     [panel setTarget:self];
+    b_genericAudioProfileInInteraction = NO;
 
     [panel runModalForWindow:o_window];
 }
 
 - (void)panel:(VLCEnterTextPanel *)panel returnValue:(NSUInteger)value text:(NSString *)text
 {
-    if (value == NSOKButton) {
-        if ([text length] > 0) {
-            NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
-            NSMutableArray * workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
+
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
+    // EQ settings
+    if (!b_genericAudioProfileInInteraction) {
+        if (value == NSOKButton && [text length] > 0) {
+            NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
             [workArray addObject:[self generatePresetString]];
             [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
             [workArray release];
@@ -494,11 +735,11 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
             [defaults synchronize];
 
             /* update VLC internals */
-            vlc_object_t *p_object= VLC_OBJECT(getAout());
+            vlc_object_t *p_object = VLC_OBJECT(getAout());
             if (p_object == NULL)
                 p_object = vlc_object_hold(pl_Get(p_intf));
 
-            var_SetString(p_object , "equalizer-preset", [[text decomposedStringWithCanonicalMapping] UTF8String]);
+            var_SetString(p_object, "equalizer-preset", [[text decomposedStringWithCanonicalMapping] UTF8String]);
             config_PutPsz(p_object, "equalizer-preset", [[text decomposedStringWithCanonicalMapping] UTF8String]);
 
             vlc_object_release(p_object);
@@ -506,18 +747,63 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
             /* update UI */
             [self updatePresetSelector];
         }
+
+    // profile settings
+    } else {
+
+        if (value != NSOKButton) {
+            [o_profile_pop selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
+            return;
+        }
+
+        NSArray *profileNames = [defaults objectForKey:@"AudioEffectProfileNames"];
+
+        // duplicate names are not allowed in the popup control
+        if ([text length] == 0 || [profileNames containsObject:text]) {
+            [o_profile_pop selectItemAtIndex:[defaults integerForKey:@"AudioEffectSelectedProfile"]];
+
+            NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+            [alert setAlertStyle:NSCriticalAlertStyle];
+            [alert setMessageText:_NS("Please enter a unique name for the new profile.")];
+            [alert setInformativeText:_NS("Multiple profiles with the same name are not allowed.")];
+
+            [alert beginSheetModalForWindow:o_window
+                              modalDelegate:nil
+                             didEndSelector:nil
+                                contextInfo:nil];
+            return;
+        }
+        
+        NSString *newProfile = [self generateProfileString];
+
+        /* add string to user defaults as well as a label */
+        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+        NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
+        [workArray addObject:newProfile];
+        [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
+        [defaults setInteger:[workArray count] - 1 forKey:@"AudioEffectSelectedProfile"];
+        [workArray release];
+        workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
+        [workArray addObject:text];
+        [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
+        [workArray release];
+
+        /* save defaults */
+        [defaults synchronize];
+        [self resetProfileSelector];
     }
 }
 
 - (IBAction)deletePresetAction:(id)sender
 {
-    VLCSelectItemInPopupPanel * panel = [VLCSelectItemInPopupPanel sharedInstance];
+    VLCSelectItemInPopupPanel *panel = [VLCSelectItemInPopupPanel sharedInstance];
     [panel setTitle:_NS("Remove a preset")];
     [panel setSubTitle:_NS("Select the preset you would like to remove:")];
     [panel setOKButtonLabel:_NS("Remove")];
     [panel setCancelButtonLabel:_NS("Cancel")];
     [panel setPopupButtonContent:[[NSUserDefaults standardUserDefaults] objectForKey:@"EQTitles"]];
     [panel setTarget:self];
+    b_genericAudioProfileInInteraction = NO;
 
     [panel runModalForWindow:o_window];
 }
@@ -525,28 +811,48 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 - (void)panel:(VLCSelectItemInPopupPanel *)panel returnValue:(NSUInteger)value item:(NSUInteger)item
 {
     if (value == NSOKButton) {
-        /* remove requested profile from the arrays */
-        NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
-        NSMutableArray * workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
-        [workArray removeObjectAtIndex:item];
-        [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
-        [workArray release];
-        workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
-        [workArray removeObjectAtIndex:item];
-        [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
-        [workArray release];
-        workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
-        [workArray removeObjectAtIndex:item];
-        [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
-        [workArray release];
-        workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
-        [workArray removeObjectAtIndex:item];
-        [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
-        [workArray release];
-        [defaults synchronize];
+        if (!b_genericAudioProfileInInteraction) {
+            /* remove requested profile from the arrays */
+            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+            NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQValues"]];
+            [workArray removeObjectAtIndex:item];
+            [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQValues"];
+            [workArray release];
+            workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
+            [workArray removeObjectAtIndex:item];
+            [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
+            [workArray release];
+            workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
+            [workArray removeObjectAtIndex:item];
+            [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
+            [workArray release];
+            workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
+            [workArray removeObjectAtIndex:item];
+            [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
+            [workArray release];
+            [defaults synchronize];
+
+            /* update UI */
+            [self updatePresetSelector];
+        } else {
+            /* remove selected profile from settings */
+            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+            NSMutableArray *workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfiles"]];
+            [workArray removeObjectAtIndex:item];
+            [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfiles"];
+            [workArray release];
+            workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"AudioEffectProfileNames"]];
+            [workArray removeObjectAtIndex:item];
+            [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"AudioEffectProfileNames"];
+            [workArray release];
+
+            if (i_old_profile_index >= item)
+                [defaults setInteger:i_old_profile_index - 1 forKey:@"AudioEffectSelectedProfile"];
 
-        /* update UI */
-        [self updatePresetSelector];
+            /* save defaults */
+            [defaults synchronize];
+            [self resetProfileSelector];
+        }
     }
 }
 
@@ -554,7 +860,7 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 #pragma mark Compressor
 - (void)resetCompressor
 {
-    char * psz_afilters;
+    char *psz_afilters;
     psz_afilters = config_GetPsz(p_intf, "audio-filter");
     if (psz_afilters) {
         [o_comp_enable_ckb setState: (NSInteger)strstr(psz_afilters, "compressor") ];
@@ -589,7 +895,7 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
     config_PutFloat(p_intf, "compressor-knee", 2.500000);
     config_PutFloat(p_intf, "compressor-makeup-gain", 7.000000);
 
-    audio_output_t * p_aout = getAout();
+    audio_output_t *p_aout = getAout();
     if (p_aout) {
         var_SetFloat(p_aout, "compressor-rms-peak", 0.000000);
         var_SetFloat(p_aout, "compressor-attack", 25.000000);
@@ -610,8 +916,8 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 
 - (IBAction)comp_sliderUpdated:(id)sender
 {
-    audio_output_t * p_aout = getAout();
-    char * value;
+    audio_output_t *p_aout = getAout();
+    char *value;
     if (sender == o_comp_band1_sld)
         value = "compressor-rms-peak";
     else if (sender == o_comp_band2_sld)
@@ -653,7 +959,7 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 #pragma mark Spatializer
 - (void)resetSpatializer
 {
-    char * psz_afilters;
+    char *psz_afilters;
     psz_afilters = config_GetPsz(p_intf, "audio-filter");
     if (psz_afilters) {
         [o_spat_enable_ckb setState: (NSInteger)strstr(psz_afilters, "spatializer") ];
@@ -663,8 +969,8 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
         [o_spat_enable_ckb setState: NSOffState];
 
 #define setSlider(bandsld, bandfld, var) \
-    [bandsld setFloatValue: config_GetFloat(p_intf, var) * 10.]; \
-    [bandfld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [bandsld floatValue]]]
+[bandsld setFloatValue: config_GetFloat(p_intf, var) * 10.]; \
+[bandfld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [bandsld floatValue]]]
 
     setSlider(o_spat_band1_sld, o_spat_band1_fld, "spatializer-roomsize");
     setSlider(o_spat_band2_sld, o_spat_band2_fld, "spatializer-width");
@@ -683,7 +989,7 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
     config_PutFloat(p_intf, "spatializer-dry", .5);
     config_PutFloat(p_intf, "spatializer-damp", .5);
 
-    audio_output_t * p_aout = getAout();
+    audio_output_t *p_aout = getAout();
     if (p_aout) {
         var_SetFloat(p_aout, "spatializer-roomsize", .85);
         var_SetFloat(p_aout, "spatializer-width", 1.);
@@ -702,8 +1008,8 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 
 - (IBAction)spat_sliderUpdated:(id)sender
 {
-    audio_output_t * p_aout = getAout();
-    char * value;
+    audio_output_t *p_aout = getAout();
+    char *value;
     if (sender == o_spat_band1_sld)
         value = "spatializer-roomsize";
     else if (sender == o_spat_band2_sld)
@@ -737,15 +1043,17 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 #pragma mark Filter
 - (void)resetAudioFilters
 {
-    char * psz_afilters;
+    char *psz_afilters;
     psz_afilters = config_GetPsz(p_intf, "audio-filter");
     if (psz_afilters) {
         [o_filter_headPhone_ckb setState: (NSInteger)strstr(psz_afilters, "headphone") ];
         [o_filter_normLevel_ckb setState: (NSInteger)strstr(psz_afilters, "normvol") ];
+        [o_filter_karaoke_ckb setState: (NSInteger)strstr(psz_afilters, "karaoke") ];
         free(psz_afilters);
     } else {
         [o_filter_headPhone_ckb setState: NSOffState];
         [o_filter_normLevel_ckb setState: NSOffState];
+        [o_filter_karaoke_ckb setState: NSOffState];
     }
     [o_filter_normLevel_sld setFloatValue: config_GetFloat(p_intf, "norm-max-level")];
 }
@@ -762,7 +1070,7 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
 
 - (IBAction)filter_volNormSliderUpdated:(id)sender
 {
-    audio_output_t * p_aout= getAout();
+    audio_output_t *p_aout = getAout();
 
     if (p_aout) {
         var_SetFloat(p_aout, "norm-max-level", [o_filter_normLevel_sld floatValue]);
@@ -772,4 +1080,9 @@ static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
     config_PutFloat(p_intf, "norm-max-level", [o_filter_normLevel_sld floatValue]);
 }
 
+- (IBAction)filter_enableKaraoke:(id)sender
+{
+    [self setAudioFilter: "karaoke" on:[sender state]];
+}
+
 @end