/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id: settings.c,v 1.409 2006-08-23 20:02:05 lowlight Exp $
 *
 * Copyright (C) 2002 by wavey@wavey.org
 * RTC config saving code (C) 2002 by hessu@hes.iki.fi
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include <stdio.h>
#include <stddef.h>
#include <limits.h>
#include "inttypes.h"
#include "config.h"
#include "kernel.h"
#include "thread.h"
#include "action.h"
#include "settings.h"
#include "disk.h"
#include "panic.h"
#include "debug.h"
#include "usb.h"
#include "backlight.h"
#include "lcd.h"
#include "audio.h"
#include "mp3_playback.h"
#include "mpeg.h"
#include "talk.h"
#include "string.h"
#include "ata.h"
#include "fat.h"
#include "power.h"
#include "powermgmt.h"
#include "status.h"
#include "atoi.h"
#include "screens.h"
#include "ctype.h"
#include "file.h"
#include "errno.h"
#include "system.h"
#include "misc.h"
#include "timefuncs.h"
#ifdef HAVE_LCD_BITMAP
#include "icons.h"
#include "font.h"
#include "peakmeter.h"
#include "hwcompat.h"
#endif
#include "lang.h"
#include "language.h"
#include "gwps.h"
#include "powermgmt.h"
#include "bookmark.h"
#include "sprintf.h"
#include "keyboard.h"
#include "version.h"
#include "rtc.h"
#include "sound.h"
#include "rbunicode.h"
#include "dircache.h"
#include "statusbar.h"
#include "splash.h"
#include "list.h"
#ifdef HAVE_LCD_COLOR
#include "backdrop.h"
#endif
#include "sound_menu.h"

#ifdef CONFIG_TUNER
#include "radio.h"
#endif
#include "setting_struct.h"
#if CONFIG_CODEC == MAS3507D
void dac_line_in(bool enable);
#endif
struct user_settings global_settings;
#ifdef HAVE_RECORDING
const char rec_base_directory[] = REC_BASE_DIR;
#endif
#if CONFIG_CODEC == SWCODEC
#include "pcmbuf.h"
#include "pcm_playback.h"
#include "dsp.h"
#endif

#ifdef HAVE_WM8758
#include "eq_menu.h"
#endif

/* this only needs to be bumped if you add a RTC setting before other existing rtc settings */
#define RTC_RAM_CONFIG_VERSION 0 

#ifdef HAVE_LCD_BITMAP
#define MAX_LINES 10
#else
#define MAX_LINES 2
#endif

#ifdef HAVE_REMOTE_LCD
#include "lcd-remote.h"
#endif

long lasttime = 0;

#if 0
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
    {7, S_O(mdb_strength), 0, "mdb strength", NULL},
    {7, S_O(mdb_harmonics), 0, "mdb harmonics", NULL},
    {9, S_O(mdb_center), 0, "mdb center", NULL},
    {9, S_O(mdb_shape), 0, "mdb shape", NULL},
    {1, S_O(mdb_enable), 0, "mdb enable", off_on},
#endif
#if CONFIG_CODEC == MAS3507D
    {1, S_O(line_in), false, "line in", off_on },
#endif
    /* voice */
    {2, S_O(talk_dir), 0, "talk dir", off_number_spell_hover },
    {2, S_O(talk_file), 0, "talk file", off_number_spell_hover },
    {1, S_O(talk_menu), true, "talk menu", off_on },

    {2, S_O(sort_file), 0, "sort files", "alpha,oldest,newest,type" },
    {2, S_O(sort_dir), 0, "sort dirs", "alpha,oldest,newest" },
    {1, S_O(id3_v1_first), 0, "id3 tag priority", "v2-v1,v1-v2"},

#ifdef HAVE_RECORDING
    /* recording */
    {1, S_O(recscreen_on), false, "recscreen on", off_on },
    {1, S_O(rec_startup), false, "rec screen on startup", off_on },
    {4, S_O(rec_timesplit), 0, "rec timesplit", /* 0...15 */
        "off,00:05,00:10,00:15,00:30,01:00,01:14,01:20,02:00,04:00,06:00,08:00,10:00,12:00,18:00,24:00" },
    {4, S_O(rec_sizesplit), 0, "rec sizesplit", /* 0...15 */
        "off,5MB,10MB,15MB,32MB,64MB,75MB,100MB,128MB,256MB,512MB,650MB,700MB,1GB,1.5GB,1.75GB" },
    {1, S_O(rec_channels), 0, "rec channels", "stereo,mono" },
    {1, S_O(rec_split_type), 0, "rec split type", "Split, Stop" },
    {1, S_O(rec_split_method), 0, "rec split method", "Time,Filesize" },

    {
#if defined(HAVE_SPDIF_IN) || defined(HAVE_FMRADIO_IN)
        2,
#else
        1,
#endif
        S_O(rec_source), 0 /* 0=mic */, "rec source",
        "mic,line"
#ifdef HAVE_SPDIF_IN
        ",spdif"
#endif
#ifdef HAVE_FMRADIO_IN
        ",fmradio"
#endif
    },
    {5, S_O(rec_prerecord_time), 0, "prerecording time", NULL }, /* 0...30 */
    {1, S_O(rec_directory), 0, /* rec_base_directory */
        "rec directory", REC_BASE_DIR ",current" },
#ifdef CONFIG_BACKLIGHT
    {2, S_O(cliplight), 0, "cliplight", "off,main,both,remote" },
#endif 
#if CONFIG_CODEC == MAS3587F
    {4, S_O(rec_mic_gain), 8, "rec mic gain", NULL },
    {4, S_O(rec_left_gain), 2 /* 0dB */, "rec left gain", NULL }, /* 0...15 */
    {4, S_O(rec_right_gain), 2 /* 0dB */, "rec right gain", NULL }, /* 0...15 */
    {3, S_O(rec_frequency), 0, /* 0=44.1kHz */
        "rec frequency", "44,48,32,22,24,16" },
    {3, S_O(rec_quality), 5 /* 192 kBit/s max */, "rec quality", NULL },
    {1, S_O(rec_editable), false, "editable recordings", off_on },
#endif /* CONFIG_CODEC == MAS3587F */

#if CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING)
#ifdef HAVE_UDA1380
    {8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */
#endif
#ifdef HAVE_TLV320
    /* TLV320 only has no mic boost or 20db mic boost */
    {1, S_O(rec_mic_gain), 0 /* 0 dB */, "rec mic gain", NULL }, /* 0db or 20db */
#endif
    {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
    {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
#if 0 /* Till samplerates are added for SWCODEC */
    {3, S_O(rec_frequency), 0, /* 0=44.1kHz */
        "rec frequency", "44,48,32,22,24,16" },
#else
    {3, S_O(rec_frequency), 0, /* 0=44.1kHz */
        "rec frequency", "44" },
#endif

    {4, S_O(rec_quality), 4 /* MP3 L3 192 kBit/s */, "rec quality", NULL },
#endif /* CONFIG_CODEC == SWCODEC && defined(HAVE_RECORDING) */

    /* values for the trigger */
    {8 | SIGNED, S_O(rec_start_thres), -35, "trigger start threshold", NULL},
    {8 | SIGNED, S_O(rec_stop_thres), -45, "trigger stop threshold", NULL},
    {4, S_O(rec_start_duration), 0, "trigger start duration", trig_durations_conf},
    {4, S_O(rec_stop_postrec), 2, "trigger stop postrec", trig_durations_conf},
    {4, S_O(rec_stop_gap), 1, "trigger min gap", trig_durations_conf},
    {4, S_O(rec_trigger_mode ), 0, "trigger mode", "off,once,repeat"},
#endif /* HAVE_RECORDING */

#ifdef HAVE_SPDIF_POWER
    {1, S_O(spdif_enable), false, "spdif enable", off_on},
#endif

    {1, S_O(next_folder), false, "folder navigation", off_on },
    {1, S_O(runtimedb), false, "gather runtime data", off_on },

#if CONFIG_CODEC == SWCODEC
    {1, S_O(replaygain), false, "replaygain", off_on },
    {2, S_O(replaygain_type), REPLAYGAIN_ALBUM, "replaygain type",
        "track,album,track shuffle" },
    {1, S_O(replaygain_noclip), false, "replaygain noclip", off_on },
    {8 | SIGNED, S_O(replaygain_preamp), 0, "replaygain preamp", NULL },
    {2, S_O(beep), 0, "beep", "off,weak,moderate,strong" },
    {2, S_O(crossfade), 0, "crossfade", "off,shuffle,track skip,always"},
    {3, S_O(crossfade_fade_in_delay), 0, "crossfade fade in delay", NULL},
    {3, S_O(crossfade_fade_out_delay), 0, "crossfade fade out delay", NULL},
    {4, S_O(crossfade_fade_in_duration), 0, "crossfade fade in duration", NULL},
    {4, S_O(crossfade_fade_out_duration), 0, "crossfade fade out duration", NULL},
    {1, S_O(crossfade_fade_out_mixmode), 0, "crossfade fade out mode", "crossfade,mix"},
    {1, S_O(crossfeed), false, "crossfeed", off_on },
    {6, S_O(crossfeed_direct_gain), 15, "crossfeed direct gain", NULL },
    {7, S_O(crossfeed_cross_gain), 60, "crossfeed cross gain", NULL },
    {8, S_O(crossfeed_hf_attenuation), 160, "crossfeed hf attenuation", NULL },
    {11, S_O(crossfeed_hf_cutoff), 700, "crossfeed hf cutoff", NULL },

    /* equalizer */
    {1, S_O(eq_enabled), false, "eq enabled", off_on },
    {8, S_O(eq_precut), 0, "eq precut", NULL },
    /* 0..32768 Hz */
    {15, S_O(eq_band0_cutoff), 60, "eq band 0 cutoff", NULL },
    {15, S_O(eq_band1_cutoff), 200, "eq band 1 cutoff", NULL },
    {15, S_O(eq_band2_cutoff), 800, "eq band 2 cutoff", NULL },
    {15, S_O(eq_band3_cutoff), 4000, "eq band 3 cutoff", NULL },
    {15, S_O(eq_band4_cutoff), 12000, "eq band 4 cutoff", NULL },
    /* 0..64 (or 0.0 to 6.4) */
    {6, S_O(eq_band0_q), 7, "eq band 0 q", NULL },
    {6, S_O(eq_band1_q), 10, "eq band 1 q", NULL },
    {6, S_O(eq_band2_q), 10, "eq band 2 q", NULL },
    {6, S_O(eq_band3_q), 10, "eq band 3 q", NULL },
    {6, S_O(eq_band4_q), 7, "eq band 4 q", NULL },
    /* -240..240 (or -24db to +24db) */
    {9|SIGNED, S_O(eq_band0_gain), 0, "eq band 0 gain", NULL },
    {9|SIGNED, S_O(eq_band1_gain), 0, "eq band 1 gain", NULL },
    {9|SIGNED, S_O(eq_band2_gain), 0, "eq band 2 gain", NULL },
    {9|SIGNED, S_O(eq_band3_gain), 0, "eq band 3 gain", NULL },
    {9|SIGNED, S_O(eq_band4_gain), 0, "eq band 4 gain", NULL },
#endif

#ifdef HAVE_DIRCACHE
    {1, S_O(dircache), false, "dircache", off_on },
    {22, S_O(dircache_size), 0, NULL, NULL },
#endif
#ifdef HAVE_TC_RAMCACHE
    {1, S_O(tagcache_ram), 0, "tagcache_ram", off_on },
#endif
    {1, S_O(tagcache_autoupdate), 0, "tagcache_autoupdate", off_on },

    {4, S_O(default_codepage), 0, "default codepage",
        "iso8859-1,iso8859-7,iso8859-8,cp1251,iso8859-11,cp1256,iso8859-9,iso8859-2,sjis,gb2312,ksx1001,big5,utf-8,cp1256" },

    {1, S_O(warnon_erase_dynplaylist), false,
        "warn when erasing dynamic playlist", off_on },

#ifdef CONFIG_BACKLIGHT
#ifdef HAS_BUTTON_HOLD
    {2, S_O(backlight_on_button_hold), 0, "backlight on button hold",
        "normal,off,on" },
#endif

#ifdef HAVE_LCD_SLEEP
    {4, S_O(lcd_sleep_after_backlight_off), 3,
        "lcd sleep after backlight off",
        "always,never,5,10,15,20,30,45,60,90" },
#endif
#endif /* CONFIG_BACKLIGHT */

#ifdef HAVE_WM8758
    {1, S_O(eq_hw_enabled), false, "eq hardware enabled", off_on },
    
    {2, S_O(eq_hw_band0_cutoff), 1, "eq hardware band 0 cutoff", "80Hz,105Hz,135Hz,175Hz" },
    {5|SIGNED, S_O(eq_hw_band0_gain), 12, "eq hardware band 0 gain", NULL },

    {2, S_O(eq_hw_band1_center), 1, "eq hardware band 1 center", "230Hz,300Hz,385Hz,500Hz" },
    {1, S_O(eq_hw_band1_bandwidth), 0, "eq hardware band 1 bandwidth", "narrow,wide" },
    {5|SIGNED, S_O(eq_hw_band1_gain), 12, "eq hardware band 1 gain", NULL },

    {2, S_O(eq_hw_band2_center), 1, "eq hardware band 2 center", "650Hz,850Hz,1.1kHz,1.4kHz" },
    {1, S_O(eq_hw_band2_bandwidth), 0, "eq hardware band 2 bandwidth", "narrow,wide" },
    {5|SIGNED, S_O(eq_hw_band2_gain), 12, "eq hardware band 2 gain", NULL },

    {2, S_O(eq_hw_band3_center), 1, "eq hardware band 3 center", "1.8kHz,2.4kHz,3.2kHz,4.1kHz" },
    {1, S_O(eq_hw_band3_bandwidth), 0, "eq hardware band 3 bandwidth", "narrow,wide" },
    {5|SIGNED, S_O(eq_hw_band3_gain), 12, "eq hardware band 3 gain", NULL },

    {2, S_O(eq_hw_band4_cutoff), 1, "eq hardware band 4 cutoff", "5.3kHz,6.9kHz,9kHz,11.7kHz" },
    {5|SIGNED, S_O(eq_hw_band4_gain), 12, "eq hardware band 4 gain", NULL },
#endif
    {1, S_O(hold_lr_for_scroll_in_list), true, "hold_lr_for_scroll_in_list", off_on },

    {2, S_O(show_path_in_browser), 0, "show path in browser", "off,current directory,full path" },
#ifdef HAVE_AGC
    {4, S_O(rec_agc_preset_mic), 1, "agc mic preset", NULL}, /* 0...5 */
    {4, S_O(rec_agc_preset_line), 1, "agc line preset", NULL}, /* 0...5 */
    {8|SIGNED, S_O(rec_agc_maxgain_mic), 104, "agc maximum mic gain", NULL},
    {8|SIGNED, S_O(rec_agc_maxgain_line), 96, "agc maximum line gain", NULL},
    {3, S_O(rec_agc_cliptime), 1, "agc cliptime", "0.2s,0.4s,0.6s,0.8,1s"},
#endif
    /* If values are just added to the end, no need to bump the version. */
    /* new stuff to be added at the end */

    /* Sum of all bit sizes must not grow beyond 0xB8*8 = 1472 */
};
#endif

#ifdef HAVE_LCD_COLOR
/*
 * Helper function to convert a string of 6 hex digits to a native colour
 */

#define hex2dec(c) (((c) >= '0' && ((c) <= '9')) ? (toupper(c)) - '0' : \
                                                   (toupper(c)) - 'A' + 10)

int hex_to_rgb(const char* hex)
{   int ok = 1;
    int i;
    int red, green, blue;

    if (strlen(hex) == 6) {
        for (i=0; i < 6; i++ ) {
           if (!isxdigit(hex[i])) {
              ok=0;
              break;
           }
        }

        if (ok) {
            red = (hex2dec(hex[0]) << 4) | hex2dec(hex[1]);
            green = (hex2dec(hex[2]) << 4) | hex2dec(hex[3]);
            blue = (hex2dec(hex[4]) << 4) | hex2dec(hex[5]);
            return LCD_RGBPACK(red,green,blue);
        }
    }

    return 0;
}
#endif

int settings_save( void )
{
   
    return 0;
}

#ifdef HAVE_LCD_BITMAP
/**
 * Applies the range infos stored in global_settings to
 * the peak meter.
 */
void settings_apply_pm_range(void)
{
    int pm_min, pm_max;

    /* depending on the scale mode (dBfs or percent) the values
       of global_settings.peak_meter_dbfs have different meanings */
    if (global_settings.peak_meter_dbfs)
    {
        /* convert to dBfs * 100          */
        pm_min = -(((int)global_settings.peak_meter_min) * 100);
        pm_max = -(((int)global_settings.peak_meter_max) * 100);
    }
    else
    {
        /* percent is stored directly -> no conversion */
        pm_min = global_settings.peak_meter_min;
        pm_max = global_settings.peak_meter_max;
    }

    /* apply the range */
    peak_meter_init_range(global_settings.peak_meter_dbfs, pm_min, pm_max);
}
#endif /* HAVE_LCD_BITMAP */

void sound_settings_apply(void)
{
    sound_set(SOUND_BASS, global_settings.bass);
    sound_set(SOUND_TREBLE, global_settings.treble);
    sound_set(SOUND_BALANCE, global_settings.balance);
    sound_set(SOUND_VOLUME, global_settings.volume);
#if CONFIG_CODEC == SWCODEC
    channels_set(global_settings.channel_config);
    stereo_width_set(global_settings.stereo_width);
#else
    sound_set(SOUND_CHANNELS, global_settings.channel_config);
    sound_set(SOUND_STEREO_WIDTH, global_settings.stereo_width);
#endif
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
    sound_set(SOUND_LOUDNESS, global_settings.loudness);
    sound_set(SOUND_AVC, global_settings.avc);
    sound_set(SOUND_MDB_STRENGTH, global_settings.mdb_strength);
    sound_set(SOUND_MDB_HARMONICS, global_settings.mdb_harmonics);
    sound_set(SOUND_MDB_CENTER, global_settings.mdb_center);
    sound_set(SOUND_MDB_SHAPE, global_settings.mdb_shape);
    sound_set(SOUND_MDB_ENABLE, global_settings.mdb_enable);
    sound_set(SOUND_SUPERBASS, global_settings.superbass);
#endif
}

void settings_apply(void)
{
    char buf[64];
#if CONFIG_CODEC == SWCODEC
    int i;
#endif
    DEBUGF( "settings_apply()\n" );
    sound_settings_apply();

    audio_set_buffer_margin(global_settings.buffer_margin);

#ifdef HAVE_LCD_CONTRAST
    lcd_set_contrast(global_settings.contrast);
#endif
    lcd_scroll_speed(global_settings.scroll_speed);
#ifdef HAVE_REMOTE_LCD
    lcd_remote_set_contrast(global_settings.remote_contrast);
    lcd_remote_set_invert_display(global_settings.remote_invert);
    lcd_remote_set_flip(global_settings.remote_flip_display);
    lcd_remote_scroll_speed(global_settings.remote_scroll_speed);
    lcd_remote_scroll_step(global_settings.remote_scroll_step);
    lcd_remote_scroll_delay(global_settings.remote_scroll_delay * (HZ/10));
    lcd_remote_bidir_scroll(global_settings.remote_bidir_limit);
#ifdef HAVE_REMOTE_LCD_TICKING
    lcd_remote_emireduce(global_settings.remote_reduce_ticking);
#endif
    remote_backlight_set_timeout(global_settings.remote_backlight_timeout);
#ifdef CONFIG_CHARGING
    remote_backlight_set_timeout_plugged(global_settings.remote_backlight_timeout_plugged);
#endif
#endif
#ifdef CONFIG_BACKLIGHT
    backlight_set_timeout(global_settings.backlight_timeout);
#ifdef CONFIG_CHARGING
    backlight_set_timeout_plugged(global_settings.backlight_timeout_plugged);
#endif
#if defined(HAVE_BACKLIGHT_PWM_FADING) && !defined(SIMULATOR)
    backlight_set_fade_in(global_settings.backlight_fade_in);
    backlight_set_fade_out(global_settings.backlight_fade_out);
#endif
#endif
#ifdef HAVE_BACKLIGHT_BRIGHTNESS
    backlight_set_brightness(global_settings.brightness);
#endif
    ata_spindown(global_settings.disk_spindown);
#if (CONFIG_CODEC == MAS3507D) && !defined(SIMULATOR)
    dac_line_in(global_settings.line_in);
#endif
    mpeg_id3_options(global_settings.id3_v1_first);
#ifdef HAVE_ATA_POWER_OFF
    ata_poweroff(global_settings.disk_poweroff);
#endif

    set_poweroff_timeout(global_settings.poweroff);

    set_battery_capacity(global_settings.battery_capacity);
#if BATTERY_TYPES_COUNT > 1
    set_battery_type(global_settings.battery_type);
#endif

#ifdef HAVE_LCD_BITMAP
    lcd_set_invert_display(global_settings.invert);
    lcd_set_flip(global_settings.flip_display);
    button_set_flip(global_settings.flip_display);
    lcd_update(); /* refresh after flipping the screen */
    settings_apply_pm_range();
    peak_meter_init_times(
        global_settings.peak_meter_release, global_settings.peak_meter_hold,
        global_settings.peak_meter_clip_hold);
#endif

#ifdef HAVE_LCD_COLOR
    unload_wps_backdrop();
#endif
    if ( global_settings.wps_file[0] &&
         global_settings.wps_file[0] != 0xff ) {
        snprintf(buf, sizeof buf, WPS_DIR "/%s.wps",
                 global_settings.wps_file);
        wps_data_load(gui_wps[0].data, buf, true);
    }
    else
    {
        wps_data_init(gui_wps[0].data);
    }

#ifdef HAVE_LCD_COLOR
    if ( global_settings.backdrop_file[0] &&
         global_settings.backdrop_file[0] != 0xff ) {
        snprintf(buf, sizeof buf, BACKDROP_DIR "/%s.bmp",
                 global_settings.backdrop_file);
        load_main_backdrop(buf);
    } else {
        unload_main_backdrop();
    }
    show_main_backdrop();

    screens[SCREEN_MAIN].set_foreground(global_settings.fg_color);
    screens[SCREEN_MAIN].set_background(global_settings.bg_color);
#endif

#if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1)
    if ( global_settings.rwps_file[0] &&
         global_settings.rwps_file[0] != 0xff ) {
        snprintf(buf, sizeof buf, WPS_DIR "/%s.rwps",
                 global_settings.rwps_file);
        wps_data_load(gui_wps[1].data, buf, true);
    }
    else
        wps_data_init(gui_wps[1].data);
#endif

#ifdef HAVE_LCD_BITMAP
    if ( global_settings.font_file[0] &&
         global_settings.font_file[0] != 0xff ) {
        snprintf(buf, sizeof buf, ROCKBOX_DIR FONT_DIR "/%s.fnt",
                 global_settings.font_file);
        font_load(buf);
    }
    else
        font_reset();

    if ( global_settings.kbd_file[0] &&
         global_settings.kbd_file[0] != 0xff ) {
        snprintf(buf, sizeof buf, ROCKBOX_DIR "/%s.kbd",
                 global_settings.kbd_file);
        load_kbd(buf);
    }
    else
        load_kbd(NULL);

    lcd_scroll_step(global_settings.scroll_step);
    gui_list_screen_scroll_step(global_settings.screen_scroll_step);
    gui_list_screen_scroll_out_of_view(global_settings.offset_out_of_view);
#else
    lcd_jump_scroll(global_settings.jump_scroll);
    lcd_jump_scroll_delay(global_settings.jump_scroll_delay * (HZ/10));
#endif
    lcd_bidir_scroll(global_settings.bidir_limit);
    lcd_scroll_delay(global_settings.scroll_delay * (HZ/10));

    if ( global_settings.lang_file[0] &&
         global_settings.lang_file[0] != 0xff ) {
        snprintf(buf, sizeof buf, ROCKBOX_DIR LANG_DIR "/%s.lng",
                 global_settings.lang_file);
        lang_load(buf);
        talk_init(); /* use voice of same language */
    }

    set_codepage(global_settings.default_codepage);

#if CONFIG_CODEC == SWCODEC
    audio_set_crossfade(global_settings.crossfade);
    dsp_set_replaygain(true);
    dsp_set_crossfeed(global_settings.crossfeed);
    dsp_set_crossfeed_direct_gain(global_settings.crossfeed_direct_gain);
    dsp_set_crossfeed_cross_params(global_settings.crossfeed_cross_gain,
                                   global_settings.crossfeed_cross_gain
                                   + global_settings.crossfeed_hf_attenuation,
                                   global_settings.crossfeed_hf_cutoff);

    dsp_set_eq(global_settings.eq_enabled);
    dsp_set_eq_precut(global_settings.eq_precut);
    /* Update all EQ bands */
    for(i = 0; i < 5; i++) {
        dsp_set_eq_coefs(i);
    }
#endif

#ifdef HAVE_WM8758
    eq_hw_enable(global_settings.eq_hw_enabled);
#endif

#ifdef HAVE_SPDIF_POWER
    spdif_power_enable(global_settings.spdif_enable);
#endif

#ifdef CONFIG_BACKLIGHT
    set_backlight_filter_keypress(global_settings.bl_filter_first_keypress);
#ifdef HAVE_REMOTE_LCD
    set_remote_backlight_filter_keypress(global_settings.remote_bl_filter_first_keypress);
#endif
#ifdef HAS_BUTTON_HOLD
    backlight_set_on_button_hold(global_settings.backlight_on_button_hold);
#endif
#ifdef HAVE_LCD_SLEEP
    lcd_set_sleep_after_backlight_off(global_settings.lcd_sleep_after_backlight_off);
#endif
#endif /* CONFIG_BACKLIGHT */
}

#define get_pointer_to_gs(v) &((uint8_t *)&global_settings)[v]

#ifdef HAVE_RTC_RAM
bool load_from_rtc(int rtc_item, int *value)
{
    if (!rtc_read_multiple(0x14+(rtc_item*4),(char*)value,4))
        return true;
    return false;
}
    
bool save_to_rtc(int rtc_item, int value)
{
    union { int i; char c[4]; } storage;
    int j = 0;
    int ret=0, address = 0x14+(rtc_item*4);
    storage.i = value;
    for(j=0; j<4 && ret==0;j++)
    {
        ret = rtc_write(address+j, storage.c[j]);
    }
    return (ret==0) ? true : false;
}
#endif
/*
 * load settings from disk or RTC RAM
 */
void settings_load(int which)
{
    DEBUGF( "reload_all_settings()\n" );
#ifdef HAVE_RTC_RAM
    if (which&SETTINGS_RTC)
    {
        int i, value, rtc_item = 0;
        bool config_ok = false;
        if (load_from_rtc(rtc_item,&value) == true)
        {
            if (value == RTC_RAM_CONFIG_VERSION)
                config_ok = true;
        }
        rtc_item++;
        for (i=0; i<nb_settings; i++)
        {
            if (settings[i].flags&F_SAVETORTC)
            {
                if (config_ok == true)
                {
                    if (load_from_rtc(rtc_item++,
                        (int*)get_pointer_to_gs(settings[i].setting_offset)) == false)
                    {
                        *(int*)get_pointer_to_gs(settings[i].setting_offset) = settings[i].default_val.int_;
                    }
                }
                else *(int*)get_pointer_to_gs(settings[i].setting_offset) = settings[i].default_val.int_;
            }
        }
    }
#endif
    if (which&SETTINGS_HD)
    {
        settings_load_config(ROCKBOX_DIR "/config.cfg");
        settings_load_config(ROCKBOX_DIR "/system.cfg");
    }
}

void set_file(char* filename, char* setting, int maxlen)
{
    char* fptr = strrchr(filename,'/');
    int len;
    int extlen = 0;
    char* ptr;

    if (!fptr)
        return;

    *fptr = 0;
    fptr++;

    len = strlen(fptr);
    ptr = fptr + len;
    while ((*ptr != '.') && (ptr != fptr)) {
        extlen++;
        ptr--;
    }
    if(ptr == fptr) extlen = 0;

    if (strncasecmp(ROCKBOX_DIR, filename ,strlen(ROCKBOX_DIR)) ||
        (len-extlen > maxlen))
        return;

    strncpy(setting, fptr, len-extlen);
    setting[len-extlen]=0;

    settings_save();
}

void save_item_to_gs(const struct settings_list *item, char* new_val)
{
    switch (item->flags&F_T_MASK)
    {
        case F_T_INT:
#ifdef HAVE_LCD_COLOR
            if (item->flags&F_STORERGB)
            {
                *(int*)get_pointer_to_gs(item->setting_offset) = hex_to_rgb(new_val);
            }
            else 
#endif
                 *(int*)get_pointer_to_gs(item->setting_offset) = atoi(new_val);
            break;
        case F_T_UINT:
            *(unsigned int*)get_pointer_to_gs(item->setting_offset) = 
                    (unsigned int)atoi(new_val);
            break;
        case F_T_BOOL:
            if (!strcmp(new_val,"on"))
                *(bool*)get_pointer_to_gs(item->setting_offset) = true;
            else *(bool*)get_pointer_to_gs(item->setting_offset) = false;
            break;
        case F_T_CHARPTR:
        case F_T_UCHARPTR:
            strcpy(*(char**)get_pointer_to_gs(item->setting_offset),new_val);
            break;
    }
}



bool settings_load_config(const char* file)
{
    int fd;
    char line[256];
    char* name;
    char* value;
    int pos; /* currently returned position */
    const struct settings_list *item;
    int len_remote = strlen("remote ");

    fd = open(file, O_RDONLY);
    if (fd < 0)
        return false;

    while (read_line(fd, line, sizeof line) > 0)
    {

        if (!settings_parseline(line, &name, &value))
            continue;
        
        for (pos = 0; pos < nb_settings ; pos++)
        {
            item = &settings[pos];
            /* check if the item is a remote item, ignore it if it is and remote doesnt match */
            if (item->flags&F_LANG_REMOTE)
            {
                if (strncmp(name,"remote ",len_remote))
                    continue;
                name += len_remote;
            }
            
            if (item->flags&(F_SYSTEMSETTING|F_STORERGB))
            {
                
                if (!strcmp(name,item->cfg_string))
                {
                    save_item_to_gs(item,value);
                    break;
                }
            }
            else
            {
                if (!strcmp(name,english_str(item->cfg_lang_id)))
                {
                    save_item_to_gs(item,value);
                    break;
                }
            }
        } /* for (...) */
    }

    close(fd);
  //  settings_apply();
 //   settings_save();
    return true;
}

bool settings_save_config(void)
{
    int fd_cfg, fd_dat, fd;
    int i, value = 0, type;
    char *name, *char_value;
    const struct settings_list *item;
    
#ifdef HAVE_RTC_RAM
    fd = 0;
    save_to_rtc(fd, RTC_RAM_CONFIG_VERSION);
    fd++;
    for (i=0; i<nb_settings; i++)
    {
        if (settings[i].flags&F_SAVETORTC)
        {
            value = *(int*)get_pointer_to_gs(settings[i].setting_offset);
            save_to_rtc(fd++, value);
        }
    }
#endif

    fd_cfg = creat(ROCKBOX_DIR "/config.cfg", O_WRONLY);
    if (fd_cfg < 0)
        return false;
    fd_dat = creat(ROCKBOX_DIR "/system.cfg", O_WRONLY);
    if (fd_dat < 0)
    {
        close(fd_cfg);
        return false;
    }
    fdprintf(fd_cfg, "# .cfg file created by rockbox %s - "
            "http://www.rockbox.org\r\n#\r\n#\r\n", appsversion);
    fdprintf(fd_dat, "# .cfg file created by rockbox %s - "
            "http://www.rockbox.org\r\n#\r\n#\r\n", appsversion);
    for (i=0; i < nb_settings; i++)
    {
        item = &settings[i];
        type = item->flags&F_T_MASK;
        if (item->flags&F_SYSTEMSETTING)
        {
            name = item->cfg_string;
            fd = fd_dat;
        }
#ifdef HAVE_LCD_COLOR
        else if (item->flags&F_STORERGB)
        {
            name = item->cfg_string;
            fd = fd_cfg;
        }
#endif
        else 
        {
            name = english_str(item->cfg_lang_id);
            fd = fd_cfg;
        }
        DEBUGF("%d, %s\n",i,name);
        char_value = NULL;
        switch (type)
        {
            case F_T_INT:
                value = *(int*)get_pointer_to_gs(item->setting_offset);
                if ((item->flags&(F_T_CHOICE|F_SYSTEMSETTING|F_STORERGB|F_T_SOUND)) == 0)
                    fdprintf(fd,"# %s - min=%d, max=%d, step=%d\r\n",name,
                             item->values[0],item->values[1],item->values[2]);
                break;
            case F_T_UINT:
                value = *(unsigned int*)get_pointer_to_gs(item->setting_offset);
                if ((item->flags&(F_T_CHOICE|F_SYSTEMSETTING|F_STORERGB|F_T_SOUND)) == 0)
                    fdprintf(fd,"# %s - min=%d, max=%d, step=%d\r\n",name,
                             item->values[0],item->values[1],item->values[2]);
                break;
            case F_T_BOOL:
                char_value = (*(bool*)get_pointer_to_gs(item->setting_offset)==true)?"on":"off";
                fdprintf(fd,"# %s - on, off\r\n",name);
                break;
            case F_T_CHARPTR:
            case F_T_UCHARPTR:
                char_value = *(char**)get_pointer_to_gs(item->setting_offset);
                break;
        }
        if (item->flags&F_T_CHOICE)
        {
            int j;
            fdprintf(fd,"# %s%s - ",(item->flags&F_LANG_REMOTE)?"remote ":"", name);
            for(j=0; j < item->nb_values; j++)
            {
                fdprintf(fd,"%s%c ",(item->flags&F_LANG_STRING) ?
                                    item->strings[j]           :
                                            english_str(item->values[j]),(j+1 < item->nb_values)?',':'\0' );
            }
            fdprintf(fd,"\r\n");
            if (item->flags&F_LANG_STRING)
                char_value = item->strings[value];
            else char_value = english_str(item->values[value]);
            
        }
#ifdef HAVE_LCD_COLOR
        if (item->flags&F_STORERGB)
        {
            fdprintf(fd, "%s: %02x%02x%02x\r\n", name,
                     (int)RGB_UNPACK_RED(value),
                     (int)RGB_UNPACK_GREEN(value),
                     (int)RGB_UNPACK_BLUE(value));
        }
        else 
#endif
        if (char_value == NULL)
        {
            fdprintf(fd,"%s%s: %d\r\n",(item->flags&F_LANG_REMOTE)?"remote ":"", name, value);
        }
        else
        {
            fdprintf(fd,"%s%s: %s\r\n",(item->flags&F_LANG_REMOTE)?"remote ":"", name, char_value);
        }
    } /* for(...) */
    close(fd_cfg);
    close(fd_dat);
    gui_syncsplash(HZ, true, str(LANG_SETTINGS_SAVED));
    return true;
}

/*
 * reset all settings to their default value
 */
void settings_reset(void) 
{
    int i; 
    const struct settings_list *item;
    DEBUGF( "settings_reset()\n" );

    for (i = 0; i < nb_settings ; i++)
    {
        item = &settings[i];
        if (item->flags&F_T_SOUND)
        {
            *(int*)get_pointer_to_gs(item->setting_offset) = sound_default(item->values[0]);
            continue;
        }
        switch (item->flags&F_T_MASK)
        {
            case F_T_INT:
                *(int*)get_pointer_to_gs(item->setting_offset) = item->default_val.int_;
                break;
            case F_T_UINT:
                *(unsigned int*)get_pointer_to_gs(item->setting_offset) = item->default_val.uint_;
                break;
            case F_T_BOOL:
                *(bool*)get_pointer_to_gs(item->setting_offset) = item->default_val.bool_;
                break;
            case F_T_CHARPTR:
            case F_T_UCHARPTR:
                strcpy(*(char**)get_pointer_to_gs(item->setting_offset),item->default_val.charptr);
                break;
        }
    } /* for (...) */
}

bool set_bool(const char* string, bool* variable )
{
    return set_bool_options(string, variable,
                            (char *)STR(LANG_SET_BOOL_YES),
                            (char *)STR(LANG_SET_BOOL_NO),
                            NULL);
}

/* wrapper to convert from int param to bool param in set_option */
static void (*boolfunction)(bool);
void bool_funcwrapper(int value)
{
    if (value)
        boolfunction(true);
    else
        boolfunction(false);
}

bool set_bool_options(const char* string, bool* variable,
                      const char* yes_str, int yes_voice,
                      const char* no_str, int no_voice,
                      void (*function)(bool))
{
    struct opt_items names[] = {
        {(unsigned char *)no_str, no_voice},
        {(unsigned char *)yes_str, yes_voice}
    };
    bool result;

    boolfunction = function;
    result = set_option(string, variable, BOOL, names, 2,
                        function ? bool_funcwrapper : NULL);
    return result;
}

void talk_unit(int unit, int value)
{
    if (global_settings.talk_menu)
    {
        if (unit < UNIT_LAST)
        {   /* use the available unit definition */
            talk_value(value, unit, false);
        }
        else
        {   /* say the number, followed by an arbitrary voice ID */
            talk_number(value, false);
            talk_id(unit, true);
        }
    }
}

struct value_setting_data {
    enum optiontype type;
    /* used for "value" settings.. */
    int max;
    int step;
    int voice_unit;
    const char * unit;
    void (*formatter)(char* dest, int dest_length,
    int variable, const char* unit);
    /* used for BOOL and "choice" settings */
    struct opt_items* options;    
};
    
char * value_setting_get_name_cb(int selected_item,void * data, char *buffer)
{
    struct value_setting_data* cb_data = 
            (struct value_setting_data*)data;
    if (cb_data->type == INT && !cb_data->options)
    {
        int item = cb_data->max -(selected_item*cb_data->step);
        if (cb_data->formatter)
            cb_data->formatter(buffer, MAX_PATH,item,cb_data->unit);
        else
            snprintf(buffer, MAX_PATH,"%d %s",item,cb_data->unit);
    }
    else strcpy(buffer,P2STR(cb_data->options[selected_item].string));
    return buffer;
} 
#define type_fromvoidptr(type, value) \
    (type == INT)? \
        (int)(*(int*)(value)) \
    : \
        (bool)(*(bool*)(value))
bool do_set_setting(const unsigned char* string, void *variable,
                    int nb_items,int selected, 
                    struct value_setting_data *cb_data,
                    void (*function)(int))
{
    int action;
    bool done = false;
    struct gui_synclist lists;
    int oldvalue;
    
    if (cb_data->type == INT)
        oldvalue = *(int*)variable;
    else oldvalue = *(bool*)variable;
    
    gui_synclist_init(&lists,value_setting_get_name_cb,(void*)cb_data,false,1);
    gui_synclist_set_title(&lists, (char*)string, NOICON);
    gui_synclist_set_icon_callback(&lists,NULL);
    gui_synclist_set_nb_items(&lists,nb_items);
    gui_synclist_limit_scroll(&lists,true);
    gui_synclist_select_item(&lists, selected);
    
    if (global_settings.talk_menu)
    {
        if (cb_data->type == INT && !cb_data->options)
            talk_unit(cb_data->voice_unit, *(int*)variable);
        else talk_id(cb_data->options[selected].voice_id, false);
    }
    
    gui_synclist_draw(&lists);
    while (!done)
    {
        
        action = get_action(CONTEXT_LIST,TIMEOUT_BLOCK); 
        if (action == ACTION_NONE)
            continue;
        
        if (gui_synclist_do_button(&lists,action))
        {
            if (global_settings.talk_menu)
            {
                int value;
                if (cb_data->type == INT && !cb_data->options)
                {
                    value = cb_data->max - 
                            gui_synclist_get_sel_pos(&lists)*cb_data->step;
                    talk_unit(cb_data->voice_unit, value);
                }
                else 
                {
                    value = gui_synclist_get_sel_pos(&lists);
                    talk_id(cb_data->options[value].voice_id, false);
                }
            }
            if (cb_data->type == INT && !cb_data->options)
                *(int*)variable = cb_data->max - 
                        gui_synclist_get_sel_pos(&lists)*cb_data->step;
            else if (cb_data->type == BOOL)
                *(bool*)variable = gui_synclist_get_sel_pos(&lists) ? true : false;
            else *(int*)variable = gui_synclist_get_sel_pos(&lists);
        }  
        else if (action == ACTION_STD_CANCEL)
        {
            gui_syncsplash(HZ/2,true,str(LANG_MENU_SETTING_CANCEL));
            if (cb_data->type == INT)
                *(int*)variable = oldvalue;
            else *(bool*)variable = (bool)oldvalue;
            done = true;
        }
        else if (action == ACTION_STD_OK)
        {
            done = true;
        }
        else if(default_event_handler(action) == SYS_USB_CONNECTED)
            return true;
        gui_syncstatusbar_draw(&statusbars, false);
        if ( function )
            function(type_fromvoidptr(cb_data->type,variable));
    }
    return false;
}
bool set_int(const unsigned char* string,
             const char* unit,
             int voice_unit,
             int* variable,
             void (*function)(int),
             int step,
             int min,
             int max,
             void (*formatter)(char*, int, int, const char*) )
{
    struct value_setting_data data = {
        INT,max, step, voice_unit,unit,formatter,NULL };
    return do_set_setting(string,variable,(max-min)/step + 1,
                          (max-*variable)/step, &data,function);
}

/* NOTE: the 'type' parameter specifies the actual type of the variable
   that 'variable' points to. not the value within. Only variables with
   type 'bool' should use parameter BOOL.

   The type separation is necessary since int and bool are fundamentally
   different and bit-incompatible types and can not share the same access
   code. */ 
bool set_option(const char* string, void* variable, enum optiontype type,
                const struct opt_items* options, int numoptions, void (*function)(int))
{
    struct value_setting_data data = {
        type,0, 0, 0,NULL,NULL,(struct opt_items*)options };
    int selected;
    if (type == BOOL)
        selected = *(bool*)variable ? 1 : 0;
    else selected = *(int*)variable;
    return do_set_setting(string,variable,numoptions,
                          selected, &data,function);
}
#define MAX_OPTIONS 64
bool load_setting_screen(struct settings_list *setting)
{
    void* variable;
    int   temp_var = 0;
    bool ret = false;
    if(!setting)
        return false;
    variable = get_pointer_to_gs(setting->setting_offset);
        
    switch (setting->flags&F_T_MASK)
    {
        case F_T_BOOL:
            if (setting->flags&F_TEMPVARIABLE)
            {
                variable = (void*)temp_var;
                *(bool*)variable = *(bool*)get_pointer_to_gs(setting->setting_offset);
                ret = set_bool(ID2P(setting->cfg_lang_id),(bool*)variable);
                if (*(bool*)variable != *(bool*)get_pointer_to_gs(setting->setting_offset))
                    *(bool*)get_pointer_to_gs(setting->setting_offset) = *(bool*)variable;
            }
            else ret = set_bool(str(setting->cfg_lang_id),(bool*)variable);
            break;
        case F_T_INT:
        case F_T_UINT:
            if (setting->flags&F_T_CHOICE)
            {
                static struct opt_items options[MAX_OPTIONS];
                int i;
                for (i=0; i<setting->nb_values && i<MAX_OPTIONS; i++)
                {
                    if (setting->flags&F_LANG_STRING)
                    {
                        options[i].string = setting->strings[i];
                        if (settings->talk_id != NULL)
                            options[i].voice_id = settings->talk_id[i];
                    }
                    else options[i].string = ID2P(setting->values[i]);
                }
                if (setting->flags&F_TEMPVARIABLE)
                {
                    variable = (void*)temp_var;
                    *(int*)variable = *(int*)get_pointer_to_gs(setting->setting_offset);
                    ret = set_option(ID2P(setting->cfg_lang_id),variable,INT,
                                     options, i, (void*)setting->function);
                    if (*(int*)variable != *(int*)get_pointer_to_gs(setting->setting_offset))
                        *(int*)get_pointer_to_gs(setting->setting_offset) = *(int*)variable;
                }
                else ret = set_option(str(setting->cfg_lang_id),variable,INT,
                                      options, i, (void*)setting->function);
            }
            else if (setting->flags&F_T_SOUND)
            {
                ret = set_sound(str(setting->cfg_lang_id),
                          (int*)variable,setting->values[0]);
            }
            else
            {
                if (setting->flags&F_TEMPVARIABLE)
                {
                    variable = (void*)temp_var;
                    *(int*)variable = *(int*)get_pointer_to_gs(setting->setting_offset);
                }
                ret = set_int(str(setting->cfg_lang_id),str(setting->talk_id[0]),setting->talk_id[0],
                              (int*)variable,(void*)setting->function,setting->values[2],
                              setting->values[0],setting->values[1],(void*)setting->values[3]);
                if (setting->flags&F_TEMPVARIABLE)
                {
                    if (*(int*)variable != *(int*)get_pointer_to_gs(setting->setting_offset))
                        *(int*)get_pointer_to_gs(setting->setting_offset) = *(int*)variable;
                }
            }
            break;
    }
    
    return ret;
}

const struct settings_list *find_setting(void* variable)
{
    int i;
    void* var;
    for (i=0; i<nb_settings ; i++)
    {
        var = get_pointer_to_gs(settings[i].setting_offset);
        if (var == variable)
            return &settings[i];
    }
    return NULL;
}
#ifdef HAVE_RECORDING
/* This array holds the record timer interval lengths, in seconds */
static const unsigned long rec_timer_seconds[] =
{
    0,        /* 0 means OFF */
    5*60,     /* 00:05 */
    10*60,    /* 00:10 */
    15*60,    /* 00:15 */
    30*60,    /* 00:30 */
    60*60,    /* 01:00 */
    74*60,    /* 74:00 */
    80*60,    /* 80:00 */
    2*60*60,  /* 02:00 */
    4*60*60,  /* 04:00 */
    6*60*60,  /* 06:00 */
    8*60*60,  /* 08:00 */
    10L*60*60, /* 10:00 */
    12L*60*60, /* 12:00 */
    18L*60*60, /* 18:00 */
    24L*60*60  /* 24:00 */
};

unsigned int rec_timesplit_seconds(void)
{
    return rec_timer_seconds[global_settings.rec_timesplit];
}

/* This array holds the record size interval lengths, in bytes */
static const unsigned long rec_size_bytes[] =
{
    0,               /* 0 means OFF */
    5*1024*1024,     /* 5MB */
    10*1024*1024,    /* 10MB */
    15*1024*1024,    /* 15MB */
    32*1024*1024,    /* 32MB */
    64*1024*1024,    /* 64MB */
    75*1024*1024,    /* 75MB */
    100*1024*1024,   /* 100MB */
    128*1024*1024,   /* 128MB */
    256*1024*1024,   /* 256MB */
    512*1024*1024,   /* 512MB */
    650*1024*1024,   /* 650MB */
    700*1024*1024,   /* 700MB */
    1024*1024*1024,  /* 1GB */
    1536*1024*1024,  /* 1.5GB */
    1792*1024*1024,  /* 1.75GB  */
};

unsigned long rec_sizesplit_bytes(void)
{
    return rec_size_bytes[global_settings.rec_sizesplit];
}
/*
 * Time strings used for the trigger durations.
 * Keep synchronous to trigger_times in settings_apply_trigger
 */
const char * const trig_durations[TRIG_DURATION_COUNT] =
{
    "0s", "1s", "2s", "5s",
    "10s", "15s", "20s", "25s", "30s",
    "1min", "2min", "5min", "10min"
};

void settings_apply_trigger(void)
{
    /* Keep synchronous to trig_durations and trig_durations_conf*/
    static const long trigger_times[TRIG_DURATION_COUNT] = {
        0, HZ, 2*HZ, 5*HZ,
        10*HZ, 15*HZ, 20*HZ, 25*HZ, 30*HZ,
        60*HZ, 2*60*HZ, 5*60*HZ, 10*60*HZ
    };

    peak_meter_define_trigger(
        global_settings.rec_start_thres,
        trigger_times[global_settings.rec_start_duration],
        MIN(trigger_times[global_settings.rec_start_duration] / 2, 2*HZ),
        global_settings.rec_stop_thres,
        trigger_times[global_settings.rec_stop_postrec],
        trigger_times[global_settings.rec_stop_gap]
    );
}
#endif
