]> 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 e37d6ff6ffa3bb3adac8a8aa339e01644f058b81..38a32c9c730811a6075d7a00f78a5bf7962a79bd 100644 (file)
@@ -1,12 +1,11 @@
 /*****************************************************************************
  * AudioEffects.m: MacOS X interface module
  *****************************************************************************
- * Copyright (C) 2004-2011 the VideoLAN team
+ * Copyright (C) 2004-2012 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
  *          Jérôme Decoodt <djc@videolan.org>
- *          
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #import "AudioEffects.h"
 #import "../../audio_filter/equalizer_presets.h"
 #import "CompatibilityFixes.h"
+#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;
@@ -48,15 +54,55 @@ static VLCAudioEffects *_o_sharedInstance = nil;
     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
 }
 
++ (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];
+
+    for (int i = 0 ; i < NB_PRESETS ; i++) {
+        workString = [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
+                      eqz_preset_10b[i].f_amp[0],
+                      eqz_preset_10b[i].f_amp[1],
+                      eqz_preset_10b[i].f_amp[2],
+                      eqz_preset_10b[i].f_amp[3],
+                      eqz_preset_10b[i].f_amp[4],
+                      eqz_preset_10b[i].f_amp[5],
+                      eqz_preset_10b[i].f_amp[6],
+                      eqz_preset_10b[i].f_amp[7],
+                      eqz_preset_10b[i].f_amp[8],
+                      eqz_preset_10b[i].f_amp[9]];
+        [workValues addObject:workString];
+        [workPreamp addObject:[NSString stringWithFormat:@"%1.f", eqz_preset_10b[i].f_preamp]];
+        [workTitles addObject:[NSString stringWithUTF8String:preset_list_text[i]]];
+        [workNames addObject:[NSString stringWithUTF8String:preset_list[i]]];
+    }
+
+    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];
+    [workPreamp release];
+    [workTitles release];
+    [workNames release];
+}
+
 - (id)init
 {
-    if (_o_sharedInstance) {
+    if (_o_sharedInstance)
         [self dealloc];
-    else {
+    else {
         p_intf = VLCIntf;
+        i_old_profile_index = -1;
         _o_sharedInstance = [super init];
     }
-    
+
     return _o_sharedInstance;
 }
 
@@ -67,10 +113,7 @@ static VLCAudioEffects *_o_sharedInstance = nil;
     [o_eq_enable_ckb setTitle:_NS("Enable")];
     [o_eq_twopass_ckb setTitle:_NS("2 Pass")];
     [o_eq_preamp_lbl setStringValue:_NS("Preamp")];
-    [o_eq_presets_popup removeAllItems];
-    for( int i = 0; i < 18 ; i++ )
-        [o_eq_presets_popup insertItemWithTitle: _NS(preset_list_text[i]) atIndex: i];
-    
+
     /* Compressor */
     [o_comp_enable_ckb setTitle:_NS("Enable dynamic range compressor")];
     [o_comp_reset_btn setTitle:_NS("Reset")];
@@ -81,7 +124,7 @@ static VLCAudioEffects *_o_sharedInstance = nil;
     [o_comp_band5_lbl setStringValue:_NS("Ratio")];
     [o_comp_band6_lbl setStringValue:_NS("Knee radius")];
     [o_comp_band7_lbl setStringValue:_NS("Makeup gain")];
-    
+
     /* Spatializer */
     [o_spat_enable_ckb setTitle:_NS("Enable Spatializer")];
     [o_spat_reset_btn setTitle:_NS("Reset")];
@@ -89,13 +132,14 @@ static VLCAudioEffects *_o_sharedInstance = nil;
     [o_spat_band2_lbl setStringValue:_NS("Width")];
     [o_spat_band3_lbl setStringValue:_NS("Wet")];
     [o_spat_band4_lbl setStringValue:_NS("Dry")];
-    [o_spat_band5_lbl setStringValue:_NS("Dump")];
-    
+    [o_spat_band5_lbl setStringValue:_NS("Damp")];
+
     /* Filter */
     [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")];
     [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"compressor"]] setLabel:_NS("Compressor")];
@@ -103,81 +147,292 @@ static VLCAudioEffects *_o_sharedInstance = nil;
     [[o_tableView tabViewItemAtIndex:[o_tableView indexOfTabViewItemWithIdentifier:@"filter"]] setLabel:_NS("Filter")];
     [o_window setTitle:_NS("Audio Effects")];
     [o_window setExcludedFromWindowsMenu:YES];
-    if (OSX_LION)
+    if (!OSX_SNOW_LEOPARD)
         [o_window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
 
     [self setupEqualizer];
     [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();
-    if( p_aout )
-        psz_tmp = var_GetNonEmptyString( p_aout, "audio-filter" );
+    audio_output_t *p_aout = getAout();
+    if (p_aout)
+        psz_tmp = var_GetNonEmptyString(p_aout, "audio-filter");
     else
-        psz_tmp = config_GetPsz( p_intf, "audio-filter" );        
-    
-    if( b_on )
-    {
-        if(! psz_tmp)
-            config_PutPsz( p_intf, "audio-filter", psz_name );
-        else if( (NSInteger)strstr( psz_tmp, psz_name ) == NO )
-        {
+        psz_tmp = config_GetPsz(p_intf, "audio-filter");
+
+    if (b_on) {
+        if (!psz_tmp)
+            config_PutPsz(p_intf, "audio-filter", psz_name);
+        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 );
+            config_PutPsz(p_intf, "audio-filter", psz_tmp);
         }
     } else {
-        if( psz_tmp )
-        {
+        if (psz_tmp) {
             psz_tmp = (char *)[[[NSString stringWithUTF8String: psz_tmp] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@":%s",psz_name]]] UTF8String];
             psz_tmp = (char *)[[[NSString stringWithUTF8String: psz_tmp] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@"%s:",psz_name]]] UTF8String];
             psz_tmp = (char *)[[[NSString stringWithUTF8String: psz_tmp] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithUTF8String:psz_name]]] UTF8String];
-            config_PutPsz( p_intf, "audio-filter", psz_tmp );
+            config_PutPsz(p_intf, "audio-filter", psz_tmp);
+        }
+    }
+
+    if (p_aout) {
+        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]);
 
-    if( p_aout ) {
-        aout_EnableFilter( pl_Get( p_intf ), psz_name, b_on );
-        vlc_object_release( p_aout );
+    /* 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 )
+static bool GetEqualizerStatus(intf_thread_t *p_custom_intf,
+                               char *psz_name)
 {
     char *psz_parser, *psz_string = NULL;
     vlc_object_t *p_object = VLC_OBJECT(getAout());
-    if( p_object == NULL )
-        p_object = vlc_object_hold(pl_Get( p_custom_intf ));
-    
-    psz_string = config_GetPsz( p_custom_intf, "audio-filter" );
-    
-    if(! psz_string )
-        psz_string = var_GetNonEmptyString( p_object, "audio-filter" );
-    
-    vlc_object_release( p_object );
-    
-    if( !psz_string ) return false;
-    
-    psz_parser = strstr( psz_string, psz_name );
-    
-    free( psz_string );
-    
-    if ( psz_parser )
+    if (p_object == NULL)
+        p_object = vlc_object_hold(pl_Get(p_custom_intf));
+
+    psz_string = config_GetPsz(p_custom_intf, "audio-filter");
+
+    if (!psz_string)
+        psz_string = var_GetNonEmptyString(p_object, "audio-filter");
+
+    vlc_object_release(p_object);
+
+    if (!psz_string)
+        return false;
+
+    psz_parser = strstr(psz_string, psz_name);
+
+    free(psz_string);
+
+    if (psz_parser)
         return true;
     else
         return false;
@@ -185,109 +440,117 @@ static bool GetEqualizerStatus( intf_thread_t *p_custom_intf,
 
 - (void)setupEqualizer
 {
-    int i;
-    vlc_object_t *p_object= VLC_OBJECT(getAout());
-    if( p_object == NULL )
-        p_object = vlc_object_hold(pl_Get( VLCIntf ));
-    
-    if( p_object )
-    {
-        char *psz_preset;
-        
-        var_Create( p_object, "equalizer-preset", VLC_VAR_STRING |
-                   VLC_VAR_DOINHERIT );
-        psz_preset = var_GetNonEmptyString( p_object, "equalizer-preset" );
-        
-        for( i = 0 ; i < NB_PRESETS ; i++ )
-        {
-            if( strcmp( preset_list[i], psz_preset ) )
-                continue;
-            
-            [o_eq_presets_popup selectItemAtIndex: i];
-            
-            
-            [o_eq_preamp_sld setFloatValue: eqz_preset_10b[i].f_preamp];
-            [self setBandSlidersValues: (float *)eqz_preset_10b[i].f_amp];
-            
-            if( strcmp( psz_preset, "flat" ) )
-            {
-                char psz_bands[100];
-                
-                snprintf( psz_bands, sizeof( psz_bands ),
-                         "%.1f %.1f %.1f %.1f %.1f %.1f %.1f "
-                         "%.1f %.1f %.1f",
-                         eqz_preset_10b[i].f_amp[0],
-                         eqz_preset_10b[i].f_amp[1],
-                         eqz_preset_10b[i].f_amp[2],
-                         eqz_preset_10b[i].f_amp[3],
-                         eqz_preset_10b[i].f_amp[4],
-                         eqz_preset_10b[i].f_amp[5],
-                         eqz_preset_10b[i].f_amp[6],
-                         eqz_preset_10b[i].f_amp[7],
-                         eqz_preset_10b[i].f_amp[8],
-                         eqz_preset_10b[i].f_amp[9] );
-                
-                var_Create( p_object, "equalizer-preamp", VLC_VAR_FLOAT |
-                           VLC_VAR_DOINHERIT );
-                var_Create( p_object, "equalizer-bands", VLC_VAR_STRING |
-                           VLC_VAR_DOINHERIT );
-                var_SetFloat( p_object, "equalizer-preamp",
-                             eqz_preset_10b[i].f_preamp );
-                var_SetString( p_object, "equalizer-bands", psz_bands );
-            }
-        }
-        free( psz_preset );
-        vlc_object_release( p_object );
+    vlc_object_t *p_object = VLC_OBJECT(getAout());
+    if (p_object == NULL)
+        p_object = vlc_object_hold(pl_Get(p_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 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),
+             "%.1f %.1f %.1f %.1f %.1f %.1f %.1f "
+             "%.1f %.1f %.1f",
+             eqz_preset_10b[currentPresetIndex].f_amp[0],
+             eqz_preset_10b[currentPresetIndex].f_amp[1],
+             eqz_preset_10b[currentPresetIndex].f_amp[2],
+             eqz_preset_10b[currentPresetIndex].f_amp[3],
+             eqz_preset_10b[currentPresetIndex].f_amp[4],
+             eqz_preset_10b[currentPresetIndex].f_amp[5],
+             eqz_preset_10b[currentPresetIndex].f_amp[6],
+             eqz_preset_10b[currentPresetIndex].f_amp[7],
+             eqz_preset_10b[currentPresetIndex].f_amp[8],
+             eqz_preset_10b[currentPresetIndex].f_amp[9]);
+
+    var_Create(p_object, "equalizer-preamp", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
+    var_Create(p_object, "equalizer-bands", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
+    var_SetFloat(p_object, "equalizer-preamp", eqz_preset_10b[currentPresetIndex].f_preamp);
+    var_SetString(p_object, "equalizer-bands", psz_bands);
+    vlc_object_release(p_object);
+
+    [self updatePresetSelector];
+
     [self equalizerUpdated];
 }
 
+- (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:)];
+
+    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));
+
+    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];
+}
+
 - (void)equalizerUpdated
 {
     float f_preamp, f_band[10];
     char *psz_bands, *psz_bands_init, *p_next;
     bool b_2p;
-    int i;
-    bool b_enabled = GetEqualizerStatus( p_intf, (char *)"equalizer" );
+    bool b_enabled = GetEqualizerStatus(p_intf, (char *)"equalizer");
+
     vlc_object_t *p_object = VLC_OBJECT(getAout());
-    
-    if( p_object == NULL )
-        p_object = vlc_object_hold(pl_Get( p_intf ));
-    
-    var_Create( p_object, "equalizer-preamp", VLC_VAR_FLOAT |
-               VLC_VAR_DOINHERIT );
-    var_Create( p_object, "equalizer-bands", VLC_VAR_STRING |
-               VLC_VAR_DOINHERIT );
-    
-    psz_bands = var_GetNonEmptyString( p_object, "equalizer-bands" );
-    
-    if( psz_bands == NULL )
-        psz_bands = strdup( "0 0 0 0 0 0 0 0 0 0" );
-
-    b_2p = (BOOL)config_GetInt( p_object, "equalizer-2pass" );
-    f_preamp = config_GetFloat( p_object, "equalizer-preamp" );
-
-    vlc_object_release( p_object );
-    
-    /* Set the preamp slider */
-    [o_eq_preamp_sld setFloatValue: f_preamp];
-
-    /* Set the bands slider */
-    psz_bands_init = psz_bands;
-    
-    for( i = 0; i < 10; i++ )
-    {
-        /* Read dB -20/20 */
-        f_band[i] = strtof( psz_bands, &p_next );
-        if( !p_next || p_next == psz_bands ) break; /* strtof() failed */
-        
-        if( !*psz_bands ) break; /* end of line */
-        psz_bands = p_next+1;
-    }
-    free( psz_bands_init );
-    [self setBandSlidersValues:f_band];
-    
+    if (p_object == NULL)
+        p_object = vlc_object_hold(pl_Get(p_intf));
+
+    var_Create(p_object, "equalizer-preamp", VLC_VAR_FLOAT |
+               VLC_VAR_DOINHERIT);
+    var_Create(p_object, "equalizer-bands", VLC_VAR_STRING |
+               VLC_VAR_DOINHERIT);
+
+    psz_bands = var_GetNonEmptyString(p_object, "equalizer-bands");
+
+    if (psz_bands == NULL)
+        psz_bands = strdup("0 0 0 0 0 0 0 0 0 0");
+
+    b_2p = (BOOL)config_GetInt(p_object, "equalizer-2pass");
+    f_preamp = config_GetFloat(p_object, "equalizer-preamp");
+
+    vlc_object_release(p_object);
+
+    /* Setup sliders */
+    [self updatePresetSelector];
+
     /* Set the the checkboxes */
     [o_eq_enable_ckb setState: b_enabled];
     [o_eq_twopass_ckb setState: b_2p];
@@ -295,8 +558,7 @@ static bool GetEqualizerStatus( intf_thread_t *p_custom_intf,
 
 - (id)sliderByIndex:(int)index
 {
-    switch(index)
-    {
+    switch(index) {
         case 0 : return o_eq_band1_sld;
         case 1 : return o_eq_band2_sld;
         case 2 : return o_eq_band3_sld;
@@ -317,16 +579,40 @@ static bool GetEqualizerStatus( intf_thread_t *p_custom_intf,
         [self setValue:values[i] forSlider:i];
 }
 
+- (void)setBandSliderValuesForPreset:(NSInteger)presetID
+{
+    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];
+}
+
 - (void)initBandSliders
 {
     for (int i = 0 ; i< 9 ; i++)
         [self setValue:0.0 forSlider:i];
 }
 
+- (NSString *)generatePresetString
+{
+    return [NSString stringWithFormat:@"%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
+            [o_eq_band1_sld floatValue],
+            [o_eq_band2_sld floatValue],
+            [o_eq_band3_sld floatValue],
+            [o_eq_band4_sld floatValue],
+            [o_eq_band5_sld floatValue],
+            [o_eq_band6_sld floatValue],
+            [o_eq_band7_sld floatValue],
+            [o_eq_band8_sld floatValue],
+            [o_eq_band9_sld floatValue],
+            [o_eq_band10_sld floatValue]];
+}
+
 - (void)setValue:(float)value forSlider:(int)index
 {
     id slider = [self sliderByIndex:index];
-    
+
     if (slider != nil)
         [slider setFloatValue:value];
 }
@@ -339,139 +625,286 @@ 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 ));
-    
-    const char *psz_values;
-    NSString *preset = [NSString stringWithFormat:@"%.1f ", [o_eq_band1_sld floatValue] ];
-    preset = [preset stringByAppendingFormat:@"%.1f ", [o_eq_band2_sld floatValue] ];
-    preset = [preset stringByAppendingFormat:@"%.1f ", [o_eq_band3_sld floatValue] ];
-    preset = [preset stringByAppendingFormat:@"%.1f ", [o_eq_band4_sld floatValue] ];
-    preset = [preset stringByAppendingFormat:@"%.1f ", [o_eq_band5_sld floatValue] ];
-    preset = [preset stringByAppendingFormat:@"%.1f ", [o_eq_band6_sld floatValue] ];
-    preset = [preset stringByAppendingFormat:@"%.1f ", [o_eq_band7_sld floatValue] ];
-    preset = [preset stringByAppendingFormat:@"%.1f ", [o_eq_band8_sld floatValue] ];
-    preset = [preset stringByAppendingFormat:@"%.1f ", [o_eq_band9_sld floatValue] ];
-    preset = [preset stringByAppendingFormat:@"%.1f", [o_eq_band10_sld floatValue] ];
-
-    psz_values = [preset UTF8String];
-    var_SetString( p_object, "equalizer-bands", psz_values );
+    if (p_object == NULL)
+        p_object = vlc_object_hold(pl_Get(p_intf));
+
+    var_SetString(p_object, "equalizer-bands", [[self generatePresetString] UTF8String]);
 
     /* save changed to config */
-    config_PutPsz( p_intf, "equalizer-bands", psz_values );
+    config_PutPsz(p_intf, "equalizer-bands", [[self generatePresetString] UTF8String]);
 
-    vlc_object_release( p_object );
+    vlc_object_release(p_object);
 }
+
 - (IBAction)eq_changePreset:(id)sender
 {
-    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" , preset_list[[sender indexOfSelectedItem]] );
-    
-    NSString *preset = @"";
-    const char *psz_values;
-    for( int i = 0; i < EQZ_BANDS_MAX; i++ )
-    {
-        preset = [preset stringByAppendingFormat:@"%.1f ", eqz_preset_10b[[sender indexOfSelectedItem]].f_amp[i] ];
-    }
-    psz_values = [preset UTF8String];
-    var_SetString( p_object, "equalizer-bands", psz_values );
-    var_SetFloat( p_object, "equalizer-preamp", eqz_preset_10b[[sender indexOfSelectedItem]].f_preamp);
-    
-    [o_eq_preamp_sld setFloatValue: eqz_preset_10b[[sender indexOfSelectedItem]].f_preamp];
-    
-    [self setBandSlidersValues:(float *)eqz_preset_10b[[sender indexOfSelectedItem]].f_amp];
-    
+    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];
+
+    NSString *preset = [[defaults objectForKey:@"EQValues"] objectAtIndex:numberOfChosenPreset];
+    NSString *preamp = [[defaults objectForKey:@"EQPreampValues"] objectAtIndex:numberOfChosenPreset];
+
+    var_SetString(p_object, "equalizer-bands", [preset UTF8String]);
+    var_SetFloat(p_object, "equalizer-preamp", [preamp floatValue]);
+    var_SetString(p_object , "equalizer-preset" , [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String]);
+
+    [o_eq_preamp_sld setFloatValue: [preamp floatValue]];
+    [self setBandSliderValuesForPreset:numberOfChosenPreset];
+
     /* save changed to config */
-    config_PutPsz( p_intf, "equalizer-bands", psz_values );
-    config_PutFloat( p_intf, "equalizer-preamp", eqz_preset_10b[[sender indexOfSelectedItem]].f_preamp );
-    config_PutPsz( p_intf, "equalizer-preset", preset_list[[sender indexOfSelectedItem]] );
+    config_PutPsz(p_intf, "equalizer-bands", [preset UTF8String]);
+    config_PutFloat(p_intf, "equalizer-preamp", [preamp floatValue]);
+    config_PutPsz(p_intf, "equalizer-preset", [[[defaults objectForKey:@"EQNames"] objectAtIndex:numberOfChosenPreset] UTF8String]);
 
-    vlc_object_release( p_object );
+    vlc_object_release(p_object);
 }
+
 - (IBAction)eq_preampSliderUpdated:(id)sender
 {
     float f_preamp = [sender floatValue] ;
 
     vlc_object_t *p_object = VLC_OBJECT(getAout());
-    if( p_object == NULL )
-        p_object = vlc_object_hold(pl_Get( p_intf ));
+    if (p_object == NULL)
+        p_object = vlc_object_hold(pl_Get(p_intf));
 
-    var_SetFloat( p_object, "equalizer-preamp", f_preamp );
+    var_SetFloat(p_object, "equalizer-preamp", f_preamp);
 
     /* save changed to config */
-    config_PutFloat( p_intf, "equalizer-preamp", f_preamp );
+    config_PutFloat(p_intf, "equalizer-preamp", f_preamp);
 
-    vlc_object_release( p_object );
+    vlc_object_release(p_object);
 }
 - (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);
-    if( p_object == NULL )
-        p_object = vlc_object_hold(pl_Get( p_intf ));
 
-    var_SetBool( p_object, "equalizer-2pass", b_2p );
+    vlc_object_t *p_object = VLC_OBJECT(getAout());
+    if (p_object == NULL)
+        p_object = vlc_object_hold(pl_Get(p_intf));
+
+    var_SetBool(p_object, "equalizer-2pass", b_2p);
 
     /* save changed to config */
-    config_PutInt( p_intf, "equalizer-2pass", (int)b_2p );
+    config_PutInt(p_intf, "equalizer-2pass", (int)b_2p);
+
+    vlc_object_release(p_object);
+}
 
-    vlc_object_release( p_object );
+- (IBAction)addPresetAction:(id)sender
+{
+    /* show panel */
+    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
+{
+
+    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];
+            workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQTitles"]];
+            [workArray addObject:text];
+            [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQTitles"];
+            [workArray release];
+            workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQPreampValues"]];
+            [workArray addObject:[NSString stringWithFormat:@"%.1f", [o_eq_preamp_sld floatValue]]];
+            [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQPreampValues"];
+            [workArray release];
+            workArray = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:@"EQNames"]];
+            [workArray addObject:[text decomposedStringWithCanonicalMapping]];
+            [defaults setObject:[NSArray arrayWithArray:workArray] forKey:@"EQNames"];
+            [workArray release];
+            [defaults synchronize];
+
+            /* update VLC internals */
+            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]);
+            config_PutPsz(p_object, "equalizer-preset", [[text decomposedStringWithCanonicalMapping] UTF8String]);
+
+            vlc_object_release(p_object);
+
+            /* 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];
+    [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];
+}
+
+- (void)panel:(VLCSelectItemInPopupPanel *)panel returnValue:(NSUInteger)value item:(NSUInteger)item
+{
+    if (value == NSOKButton) {
+        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"];
+
+            /* save defaults */
+            [defaults synchronize];
+            [self resetProfileSelector];
+        }
+    }
 }
 
 #pragma mark -
 #pragma mark Compressor
 - (void)resetCompressor
 {
-    char * psz_afilters;
-    psz_afilters = config_GetPsz( p_intf, "audio-filter" );
-    if( psz_afilters ) {
-        [o_comp_enable_ckb setState: (NSInteger)strstr( psz_afilters, "compressor" ) ];
-        free( 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") ];
+        free(psz_afilters);
+    }
     else
         [o_comp_enable_ckb setState: NSOffState];
 
-    [o_comp_band1_sld setFloatValue: config_GetFloat( p_intf, "compressor-rms-peak" )];
+    [o_comp_band1_sld setFloatValue: config_GetFloat(p_intf, "compressor-rms-peak")];
     [o_comp_band1_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [o_comp_band1_sld floatValue]]];
-    [o_comp_band2_sld setFloatValue: config_GetFloat( p_intf, "compressor-attack" )];
+    [o_comp_band2_sld setFloatValue: config_GetFloat(p_intf, "compressor-attack")];
     [o_comp_band2_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [o_comp_band2_sld floatValue]]];
-    [o_comp_band3_sld setFloatValue: config_GetFloat( p_intf, "compressor-release" )];
+    [o_comp_band3_sld setFloatValue: config_GetFloat(p_intf, "compressor-release")];
     [o_comp_band3_fld setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [o_comp_band3_sld floatValue]]];
-    [o_comp_band4_sld setFloatValue: config_GetFloat( p_intf, "compressor-threshold" )];
+    [o_comp_band4_sld setFloatValue: config_GetFloat(p_intf, "compressor-threshold")];
     [o_comp_band4_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [o_comp_band4_sld floatValue]]];
-    [o_comp_band5_sld setFloatValue: config_GetFloat( p_intf, "compressor-ratio" )];
+    [o_comp_band5_sld setFloatValue: config_GetFloat(p_intf, "compressor-ratio")];
     [o_comp_band5_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [o_comp_band5_sld floatValue]]];
-    [o_comp_band6_sld setFloatValue: config_GetFloat( p_intf, "compressor-knee" )];
+    [o_comp_band6_sld setFloatValue: config_GetFloat(p_intf, "compressor-knee")];
     [o_comp_band6_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [o_comp_band6_sld floatValue]]];
-    [o_comp_band7_sld setFloatValue: config_GetFloat( p_intf, "compressor-makeup-gain" )];
+    [o_comp_band7_sld setFloatValue: config_GetFloat(p_intf, "compressor-makeup-gain")];
     [o_comp_band7_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [o_comp_band7_sld floatValue]]];
 }
 
 - (IBAction)resetCompressorValues:(id)sender
 {
-    config_PutFloat( p_intf, "compressor-rms-peak", 0.000000 );
-    config_PutFloat( p_intf, "compressor-attack", 25.000000 );
-    config_PutFloat( p_intf, "compressor-release", 100.000000 );
-    config_PutFloat( p_intf, "compressor-threshold", -11.000000 );
-    config_PutFloat( p_intf, "compressor-ratio", 8.000000 );
-    config_PutFloat( p_intf, "compressor-knee", 2.500000 );
-    config_PutFloat( p_intf, "compressor-makeup-gain", 7.000000 );
+    config_PutFloat(p_intf, "compressor-rms-peak", 0.000000);
+    config_PutFloat(p_intf, "compressor-attack", 25.000000);
+    config_PutFloat(p_intf, "compressor-release", 100.000000);
+    config_PutFloat(p_intf, "compressor-threshold", -11.000000);
+    config_PutFloat(p_intf, "compressor-ratio", 8.000000);
+    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 );
-        var_SetFloat( p_aout, "compressor-release", 100.000000 );
-        var_SetFloat( p_aout, "compressor-threshold", -11.000000 );
-        var_SetFloat( p_aout, "compressor-ratio", 8.000000 );
-        var_SetFloat( p_aout, "compressor-knee", 2.500000 );
-        var_SetFloat( p_aout, "compressor-makeup-gain", 7.000000 );
-        vlc_object_release( p_aout );
+        var_SetFloat(p_aout, "compressor-rms-peak", 0.000000);
+        var_SetFloat(p_aout, "compressor-attack", 25.000000);
+        var_SetFloat(p_aout, "compressor-release", 100.000000);
+        var_SetFloat(p_aout, "compressor-threshold", -11.000000);
+        var_SetFloat(p_aout, "compressor-ratio", 8.000000);
+        var_SetFloat(p_aout, "compressor-knee", 2.500000);
+        var_SetFloat(p_aout, "compressor-makeup-gain", 7.000000);
+        vlc_object_release(p_aout);
     }
     [self resetCompressor];
 }
@@ -483,42 +916,42 @@ static bool GetEqualizerStatus( intf_thread_t *p_custom_intf,
 
 - (IBAction)comp_sliderUpdated:(id)sender
 {
-    audio_output_t * p_aout = getAout();
-    char * value;
-    if( sender == o_comp_band1_sld )
+    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 )
+    else if (sender == o_comp_band2_sld)
         value = "compressor-attack";
-    else if( sender == o_comp_band3_sld )
+    else if (sender == o_comp_band3_sld)
         value = "compressor-release";
-    else if( sender == o_comp_band4_sld )
+    else if (sender == o_comp_band4_sld)
         value = "compressor-threshold";
-    else if( sender == o_comp_band5_sld )
+    else if (sender == o_comp_band5_sld)
         value = "compressor-ratio";
-    else if( sender == o_comp_band6_sld )
+    else if (sender == o_comp_band6_sld)
         value = "compressor-knee";
-    else if( sender == o_comp_band7_sld )
+    else if (sender == o_comp_band7_sld)
         value = "compressor-makeup-gain";
 
-    if( p_aout ) {
-        var_SetFloat( p_aout, value, [sender floatValue] );
-        vlc_object_release( p_aout );
+    if (p_aout) {
+        var_SetFloat(p_aout, value, [sender floatValue]);
+        vlc_object_release(p_aout);
     }
-    config_PutFloat( p_intf, value, [sender floatValue] );
+    config_PutFloat(p_intf, value, [sender floatValue]);
 
-    if( sender == o_comp_band1_sld )
+    if (sender == o_comp_band1_sld)
         [o_comp_band1_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
-    else if( sender == o_comp_band2_sld )
+    else if (sender == o_comp_band2_sld)
         [o_comp_band2_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f ms", [sender floatValue]]];
-    else if( sender == o_comp_band3_sld )
+    else if (sender == o_comp_band3_sld)
         [o_comp_band3_fld setStringValue:[NSString localizedStringWithFormat:@"%3.1f ms", [sender floatValue]]];
-    else if( sender == o_comp_band4_sld )
+    else if (sender == o_comp_band4_sld)
         [o_comp_band4_fld setStringValue:[NSString localizedStringWithFormat:@"%2.1f dB", [sender floatValue]]];
-    else if( sender == o_comp_band5_sld )
+    else if (sender == o_comp_band5_sld)
         [o_comp_band5_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f:1", [sender floatValue]]];
-    else if( sender == o_comp_band6_sld )
+    else if (sender == o_comp_band6_sld)
         [o_comp_band6_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [sender floatValue]]];
-    else if( sender == o_comp_band7_sld )
+    else if (sender == o_comp_band7_sld)
         [o_comp_band7_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f dB", [sender floatValue]]];
 }
 
@@ -526,82 +959,83 @@ static bool GetEqualizerStatus( intf_thread_t *p_custom_intf,
 #pragma mark Spatializer
 - (void)resetSpatializer
 {
-    char * psz_afilters;
-    psz_afilters = config_GetPsz( p_intf, "audio-filter" );
-    if( psz_afilters ) {
-        [o_spat_enable_ckb setState: (NSInteger)strstr( psz_afilters, "spatializer" ) ];
-        free( 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") ];
+        free(psz_afilters);
+    }
     else
         [o_spat_enable_ckb setState: NSOffState];
-    
-    [o_spat_band1_sld setFloatValue: config_GetFloat( p_intf, "spatializer-roomsize" )];
-    [o_spat_band1_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [o_spat_band1_sld floatValue]]];
-    [o_spat_band2_sld setFloatValue: config_GetFloat( p_intf, "spatializer-width" )];
-    [o_spat_band2_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [o_spat_band2_sld floatValue]]];
-    [o_spat_band3_sld setFloatValue: config_GetFloat( p_intf, "spatializer-wet" )];
-    [o_spat_band3_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [o_spat_band3_sld floatValue]]];
-    [o_spat_band4_sld setFloatValue: config_GetFloat( p_intf, "spatializer-dry" )];
-    [o_spat_band4_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [o_spat_band4_sld floatValue]]];
-    [o_spat_band5_sld setFloatValue: config_GetFloat( p_intf, "spatializer-damp" )];
-    [o_spat_band5_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [o_spat_band5_sld floatValue]]];
+
+#define setSlider(bandsld, bandfld, var) \
+[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");
+    setSlider(o_spat_band3_sld, o_spat_band3_fld, "spatializer-wet");
+    setSlider(o_spat_band4_sld, o_spat_band4_fld, "spatializer-dry");
+    setSlider(o_spat_band5_sld, o_spat_band5_fld, "spatializer-damp");
+
+#undef setSlider
 }
 
 - (IBAction)resetSpatializerValues:(id)sender
 {
-    config_PutFloat( p_intf, "spatializer-roomsize", 1.050000 );
-    config_PutFloat( p_intf, "spatializer-width", 10.000000 );
-    config_PutFloat( p_intf, "spatializer-wet", 3.000000 );
-    config_PutFloat( p_intf, "spatializer-dry", 2.000000 );
-    config_PutFloat( p_intf, "spatializer-damp", 1.000000 );
+    config_PutFloat(p_intf, "spatializer-roomsize", .85);
+    config_PutFloat(p_intf, "spatializer-width", 1.);
+    config_PutFloat(p_intf, "spatializer-wet", .4);
+    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", 1.050000 );
-        var_SetFloat( p_aout, "spatializer-width", 10.000000 );
-        var_SetFloat( p_aout, "spatializer-wet", 3.000000 );
-        var_SetFloat( p_aout, "spatializer-dry", 2.000000 );
-        var_SetFloat( p_aout, "spatializer-damp", 1.000000 );
-        vlc_object_release( p_aout );
+        var_SetFloat(p_aout, "spatializer-roomsize", .85);
+        var_SetFloat(p_aout, "spatializer-width", 1.);
+        var_SetFloat(p_aout, "spatializer-wet", .4);
+        var_SetFloat(p_aout, "spatializer-dry", .5);
+        var_SetFloat(p_aout, "spatializer-damp", .5);
+        vlc_object_release(p_aout);
     }
     [self resetSpatializer];
 }
 
 - (IBAction)spat_enable:(id)sender
 {
-    [self setAudioFilter:"spatializer" on:[sender state]];    
+    [self setAudioFilter:"spatializer" on:[sender state]];
 }
 
 - (IBAction)spat_sliderUpdated:(id)sender
 {
-    audio_output_t * p_aout = getAout();
-    char * value;
-    if( sender == o_spat_band1_sld )
+    audio_output_t *p_aout = getAout();
+    char *value;
+    if (sender == o_spat_band1_sld)
         value = "spatializer-roomsize";
-    else if( sender == o_spat_band2_sld )
+    else if (sender == o_spat_band2_sld)
         value = "spatializer-width";
-    else if( sender == o_spat_band3_sld )
+    else if (sender == o_spat_band3_sld)
         value = "spatializer-wet";
-    else if( sender == o_spat_band4_sld )
+    else if (sender == o_spat_band4_sld)
         value = "spatializer-dry";
-    else if( sender == o_spat_band5_sld )
+    else if (sender == o_spat_band5_sld)
         value = "spatializer-damp";
-    
-    if( p_aout ) {
-        var_SetFloat( p_aout, value, [sender floatValue] );
-        vlc_object_release( p_aout );
+
+    if (p_aout) {
+        var_SetFloat(p_aout, value, [sender floatValue] / 10.);
+        vlc_object_release(p_aout);
     }
-    config_PutFloat( p_intf, value, [sender floatValue] );
-    
-    if( sender == o_spat_band1_sld )
+    config_PutFloat(p_intf, value, [sender floatValue] / 10.);
+
+    if (sender == o_spat_band1_sld)
         [o_spat_band1_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
-    else if( sender == o_spat_band2_sld )
+    else if (sender == o_spat_band2_sld)
         [o_spat_band2_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
-    else if( sender == o_spat_band3_sld )
+    else if (sender == o_spat_band3_sld)
         [o_spat_band3_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
-    else if( sender == o_spat_band4_sld )
+    else if (sender == o_spat_band4_sld)
         [o_spat_band4_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
-    else if( sender == o_spat_band5_sld )
+    else if (sender == o_spat_band5_sld)
         [o_spat_band5_fld setStringValue:[NSString localizedStringWithFormat:@"%1.1f", [sender floatValue]]];
 }
 
@@ -609,18 +1043,19 @@ static bool GetEqualizerStatus( intf_thread_t *p_custom_intf,
 #pragma mark Filter
 - (void)resetAudioFilters
 {
-    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" ) ];
-        free( 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" )];
+    [o_filter_normLevel_sld setFloatValue: config_GetFloat(p_intf, "norm-max-level")];
 }
 
 - (IBAction)filter_enableHeadPhoneVirt:(id)sender
@@ -630,20 +1065,24 @@ static bool GetEqualizerStatus( intf_thread_t *p_custom_intf,
 
 - (IBAction)filter_enableVolumeNorm:(id)sender
 {
-    [self setAudioFilter: "normvol" on:[sender state]];    
+    [self setAudioFilter: "normvol" on:[sender state]];
 }
 
 - (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] );
-        vlc_object_release( p_aout );
+    if (p_aout) {
+        var_SetFloat(p_aout, "norm-max-level", [o_filter_normLevel_sld floatValue]);
+        vlc_object_release(p_aout);
     }
 
-    config_PutFloat( p_intf, "norm-max-level", [o_filter_normLevel_sld floatValue] );
+    config_PutFloat(p_intf, "norm-max-level", [o_filter_normLevel_sld floatValue]);
+}
+
+- (IBAction)filter_enableKaraoke:(id)sender
+{
+    [self setAudioFilter: "karaoke" on:[sender state]];
 }
 
 @end