Ejemplo n.º 1
0
void option_talk_value(const struct settings_list *setting, int value, bool enqueue)
{
    if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
    {
        bool val = (value==1);
        talk_id(val? setting->bool_setting->lang_yes :
                setting->bool_setting->lang_no, enqueue);
    }
#if 0 /* probably dont need this one */
    else if ((setting->flags & F_FILENAME) == F_FILENAME)
    {
}
#endif
    else if (((setting->flags & F_INT_SETTING) == F_INT_SETTING) ||
               ((setting->flags & F_TABLE_SETTING) == F_TABLE_SETTING))
    {
        const struct int_setting *int_info = setting->int_setting;
        const struct table_setting *tbl_info = setting->table_setting;
        int unit;
        int32_t (*get_talk_id)(int, int);
        if ((setting->flags & F_INT_SETTING) == F_INT_SETTING)
        {
            unit = int_info->unit;
            get_talk_id = int_info->get_talk_id;
        }
        else
        {
            unit = tbl_info->unit;
            get_talk_id = tbl_info->get_talk_id;
        }
        if (get_talk_id)
            talk_id(get_talk_id(value, unit), enqueue);
        else
            talk_value(value, unit, enqueue);
    }
    else if ((setting->flags & F_T_SOUND) == F_T_SOUND)
    {
        int talkunit = UNIT_INT;
        const char *unit = sound_unit(setting->sound_setting->setting);
        if (!strcmp(unit, "dB"))
            talkunit = UNIT_DB;
        else if (!strcmp(unit, "%"))
            talkunit = UNIT_PERCENT;
        else if (!strcmp(unit, "Hz"))
            talkunit = UNIT_HERTZ;
        talk_value(value, talkunit, false);
    }
    else if ((setting->flags & F_CHOICE_SETTING) == F_CHOICE_SETTING)
    {
        if (setting->flags & F_CHOICETALKS)
        {
            talk_id(setting->choice_setting->talks[value], enqueue);
        }
        else
        {
            talk_id(P2ID(setting->choice_setting->desc[value]), enqueue);
        }
    }
}
Ejemplo n.º 2
0
static void init_tagcache(void)
{
    bool clear = false;
#if CONFIG_CODEC == SWCODEC
    long talked_tick = 0;
#endif
    tagcache_init();
    
    while (!tagcache_is_initialized())
    {
        int ret = tagcache_get_commit_step();

        if (ret > 0)
        {
#if CONFIG_CODEC == SWCODEC
            /* hwcodec can't use voice here, as the database commit
             * uses the audio buffer. */
            if(global_settings.talk_menu
               && (talked_tick == 0
                   || TIME_AFTER(current_tick, talked_tick+7*HZ)))
            {
                talked_tick = current_tick;
                talk_id(LANG_TAGCACHE_INIT, false);
                talk_number(ret, true);
                talk_id(VOICE_OF, true);
                talk_number(tagcache_get_max_commit_step(), true);
            }
#endif
#ifdef HAVE_LCD_BITMAP
            if (lang_is_rtl())
            {
                splashf(0, "[%d/%d] %s", ret, tagcache_get_max_commit_step(),
                    str(LANG_TAGCACHE_INIT));
            }
            else
            {
                splashf(0, "%s [%d/%d]", str(LANG_TAGCACHE_INIT), ret,
                    tagcache_get_max_commit_step());
            }
#else
            lcd_double_height(false);
            lcd_putsf(0, 1, " DB [%d/%d]", ret, 
                tagcache_get_max_commit_step());
            lcd_update();
#endif
            clear = true;
        }
        sleep(HZ/4);
    }
    tagtree_init();

    if (clear)
    {
        backlight_on();
        show_logo();
    }
}
Ejemplo n.º 3
0
/* ------------------------------------------------------------------------*/
static void say_bookmark(const char* bookmark,
                         int bookmark_id, bool show_playlist_name)
{
    if (!parse_bookmark(bookmark, true))
    {
        talk_id(LANG_BOOKMARK_INVALID, false);
        return;
    }

    talk_number(bookmark_id + 1, false);

#if CONFIG_CODEC == SWCODEC
    bool is_dir = (global_temp_buffer[0]
              && global_temp_buffer[strlen(global_temp_buffer)-1] == '/');

    /* HWCODEC cannot enqueue voice file entries and .talk thumbnails
       together, because there is no guarantee that the same mp3
       parameters are used. */
    if(show_playlist_name)
    {   /* It's useful to know which playlist this is */
        if(is_dir)
            talk_dir_or_spell(global_temp_buffer,
                              TALK_IDARRAY(VOICE_DIR), true);
        else talk_file_or_spell(NULL, global_temp_buffer,
                                TALK_IDARRAY(LANG_PLAYLIST), true);
    }
#else
    (void)show_playlist_name;
#endif

    if(bm.shuffle)
        talk_id(LANG_SHUFFLE, true);

    talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true);
    talk_number(bm.resume_index + 1, true);
    talk_id(LANG_TIME, true);
    talk_value(bm.resume_time / 1000, UNIT_TIME, true);

#if CONFIG_CODEC == SWCODEC
    /* Track filename */
    if(is_dir)
        talk_file_or_spell(global_temp_buffer, global_filename,
                           TALK_IDARRAY(VOICE_FILE), true);
    else
    {   /* Unfortunately if this is a playlist, we do not know in which
           directory the file is and therefore cannot find the track's
           .talk file. */
        talk_id(VOICE_FILE, true);
        talk_spell(global_filename, true);
    }
#endif
}
static int playlist_callback_voice(int selected_item, void *data)
{
    struct playlist_viewer *local_viewer = (struct playlist_viewer *)data;

    int track_num = get_track_num(local_viewer, selected_item);
    struct playlist_entry *track =
        playlist_buffer_get_track(&(local_viewer->buffer), track_num);

    bool enqueue = false;

    if (global_settings.talk_file_clip || global_settings.talk_file == 2)
    {
        if (global_settings.playlist_viewer_indices)
        {
            talk_number(track->display_index, false);
            enqueue = true;
        }
        talk_file_or_spell(NULL, track->name, NULL, enqueue);
    }
    else if (global_settings.talk_file == 1) /* as numbers */
    {
        talk_id(VOICE_FILE, false);
        talk_number(track->display_index, true);
    }

    return 0;
}
Ejemplo n.º 5
0
/* have to do this manually because the setting screen
   doesnt handle variable item count */
static int alarm_setting(void)
{
    struct opt_items items[ALARM_START_COUNT];
    int i = 0;
    items[i].string = str(LANG_RESUME_PLAYBACK);
    items[i].voice_id = LANG_RESUME_PLAYBACK;
    i++;
#if CONFIG_TUNER
    if (radio_hardware_present())
    {
        items[i].string = str(LANG_FM_RADIO);
        items[i].voice_id = LANG_FM_RADIO;
        i++;
    }
#endif
#ifdef HAVE_RECORDING
    items[i].string = str(LANG_RECORDING);
    items[i].voice_id = LANG_RECORDING;
    i++;
#endif
    return set_option(str(LANG_ALARM_WAKEUP_SCREEN),
                      &global_settings.alarm_wake_up_screen, 
                      INT, items, i, NULL);
}

MENUITEM_FUNCTION(alarm_wake_up_screen, 0, ID2P(LANG_ALARM_WAKEUP_SCREEN),
                  alarm_setting, NULL, alarm_callback, Icon_Menu_setting);
#endif /* CONFIG_TUNER || defined(HAVE_RECORDING) */

#endif /* HAVE_RTC_ALARM */
static void talk_timedate(void)
{
    struct tm *tm = get_time();
    if (!global_settings.talk_menu)
        return;
    talk_id(VOICE_CURRENT_TIME, false);
    if (valid_time(tm))
    {
        talk_time(tm, true);
        talk_date(get_time(), true);
    }
    else
    {
        talk_id(LANG_UNKNOWN, true);
    }
}
Ejemplo n.º 6
0
static int gainitem_speak_item(int selected_item, void *data)
{
    (void)data;
    talk_number(global_settings.eq_band_settings[selected_item].cutoff, false);
    talk_id(LANG_EQUALIZER_GAIN_ITEM, true);
    return 0;
}
Ejemplo n.º 7
0
static void talk_qs_option(const struct settings_list *opt, bool enqueue)
{
    if (global_settings.talk_menu) {
        if (!enqueue)
            talk_shutup();
        talk_id(opt->lang_id, true);
        option_talk_value(opt, option_value_as_int(opt), true);
    }
}
Ejemplo n.º 8
0
/* Speak a frequency. */
void talk_freq(int freq, bool enqueue)
{
    freq /= 10000;
    talk_number(freq / 100, enqueue);
    talk_id(LANG_POINT, true);
    talk_number(freq % 100 / 10, true);
    if (freq % 10)
        talk_number(freq % 10, true);
}
Ejemplo n.º 9
0
static int advancedmenu_speak_item(int selected_item, void *data)
{
    (void)data;
    int band;
    int item;
    int lang = -1;

    selection_to_banditem(selected_item, *(intptr_t*)data, &band, &item);

    switch (item)
    {
    case 0: /* Band title */
        if (band == 0)
            lang = LANG_EQUALIZER_BAND_LOW_SHELF;
        else if (band == EQ_NUM_BANDS - 1)
            lang = LANG_EQUALIZER_BAND_HIGH_SHELF;
        else
        {
            talk_id(LANG_EQUALIZER_BAND_PEAK, false);
            talk_number(band, true);
            return -1;
        }
        break;
    case 1: /* cutoff */
        if (band == 0)
            lang = LANG_EQUALIZER_BAND_CUTOFF;
        else if (band == EQ_NUM_BANDS - 1)
            lang = LANG_EQUALIZER_BAND_CUTOFF;
        else
            lang = LANG_EQUALIZER_BAND_CENTER;
        break;
    case 2: /* Q */
        lang = LANG_EQUALIZER_BAND_Q;
        break;
    case 3: /* Gain */
        lang = LANG_GAIN;
        break;
    }
    talk_id(lang, true);
    return -1;
}
Ejemplo n.º 10
0
static void say_filetype(int attr)
{
    /* try to find a voice ID for the extension, if known */
    int j;
    attr &= FILE_ATTR_MASK; /* file type */
    for (j=0; j<filetypes_count; j++)
        if (attr == filetypes[j].tree_attr)
        {
            talk_id(filetypes[j].voiceclip, true);
            return;
        }
}
/* display number of tracks inserted into playlists.  Used for directory
   insert */
static void display_insert_count(int count)
{
    static long talked_tick = 0;
    if(global_settings.talk_menu && count && 
        (talked_tick == 0 || TIME_AFTER(current_tick, talked_tick+5*HZ)))
    {
        talked_tick = current_tick;
        talk_number(count, false);
        talk_id(LANG_PLAYLIST_INSERT_COUNT, true);
    }

    splashf(0, str(LANG_PLAYLIST_INSERT_COUNT), count, str(LANG_OFF_ABORT));
}
Ejemplo n.º 12
0
static int bookmark_list_voice_cb(int list_index, void* data)
{
    struct bookmark_list* bookmarks = (struct bookmark_list*) data;
    int index = list_index / 2;

    if (bookmarks->show_dont_resume)
    {
        if (index == 0)
            return talk_id(LANG_BOOKMARK_DONT_RESUME, false);
        index--;
    }
    say_bookmark(bookmarks->items[index - bookmarks->start], index,
                 bookmarks->show_playlist_name);
    return 0;
}
Ejemplo n.º 13
0
/* 
 * Move the cursor to a particular id, 
 *   target: where you want it to be 
 */
static void put_cursor(int m, int target)
{
    int voice_id;
    
    menus[m].cursor = target;
    menu_draw(m);

    /* "say" the entry under the cursor */
    if(global_settings.talk_menu)
    {
        voice_id = P2ID(menus[m].items[menus[m].cursor].desc);
        if (voice_id >= 0) /* valid ID given? */
            talk_id(voice_id, false); /* say it */
    }
}
Ejemplo n.º 14
0
static void talk_text_message(const struct text_message * message, bool enqueue)
{
    int line;
    if(message)
    {
        for(line=0; line<message->nb_lines; line++)
        {
            long id = P2ID((unsigned char *)message->message_lines[line]);
            if(id>=0)
            {
                talk_id(id, enqueue);
                enqueue = true;
            }
        }
    }
}
Ejemplo n.º 15
0
static int ft_play_dirname(char* name)
{
#if CONFIG_CODEC != SWCODEC
    if (audio_status() & AUDIO_STATUS_PLAY)
        return 0;
#endif

    if(talk_file(tc.currdir, name, dir_thumbnail_name, NULL,
                 NULL, false))
    {
        if(global_settings.talk_filetype)
            talk_id(VOICE_DIR, true);
        return 1;
    }
    else
        return -1;
}
Ejemplo n.º 16
0
static int browser(void* param)
{
    int ret_val;
#ifdef HAVE_TAGCACHE
    struct tree_context* tc = tree_get_context();
#endif
    int filter = SHOW_SUPPORTED;
    char folder[MAX_PATH] = "/";
    /* stuff needed to remember position in file browser */
    static char last_folder[MAX_PATH] = "/";
    /* and stuff for the database browser */
#ifdef HAVE_TAGCACHE
    static int last_db_dirlevel = 0, last_db_selection = 0;
#endif
    
    switch ((intptr_t)param)
    {
        case GO_TO_FILEBROWSER:
            filter = global_settings.dirfilter;
            if (global_settings.browse_current && 
                    last_screen == GO_TO_WPS &&
                    current_track_path[0])
            {
                strcpy(folder, current_track_path);
            }
            else if (!strcmp(last_folder, "/"))
            {
                strcpy(folder, global_settings.start_directory);
            }
            else
            {
#ifdef HAVE_HOTSWAP
                bool in_hotswap = false;
                /* handle entering an ejected drive */
                int i;
                for (i = 0; i < NUM_VOLUMES; i++)
                {
                    char vol_string[VOL_ENUM_POS + 8];
                    if (!storage_removable(i))
                        continue;
                    /* VOL_NAMES contains a %d */
                    snprintf(vol_string, sizeof(vol_string), "/"VOL_NAMES, i);
                    /* test whether we would browse the external card */
                    if (!storage_present(i) &&
                            (strstr(last_folder, vol_string)
#ifdef HAVE_HOTSWAP_STORAGE_AS_MAIN
                                                                || (i == 0)
#endif
                                                                ))
                    {   /* leave folder as "/" to avoid crash when trying
                         * to access an ejected drive */
                        strcpy(folder, "/");
                        in_hotswap = true;
                        break;
                    }
                }
                if (!in_hotswap)
#endif
                    strcpy(folder, last_folder);
            }
        break;
#ifdef HAVE_TAGCACHE
        case GO_TO_DBBROWSER:
            if (!tagcache_is_usable())
            {
                bool reinit_attempted = false;

                /* Now display progress until it's ready or the user exits */
                while(!tagcache_is_usable())
                {
                    struct tagcache_stat *stat = tagcache_get_stat();                
    
                    /* Allow user to exit */
                    if (action_userabort(HZ/2))
                        break;

                    /* Maybe just needs to reboot due to delayed commit */
                    if (stat->commit_delayed)
                    {
                        splash(HZ*2, ID2P(LANG_PLEASE_REBOOT));
                        break;
                    }

                    /* Check if ready status is known */
                    if (!stat->readyvalid)
                    {
                        splash(0, str(LANG_TAGCACHE_BUSY));
                        continue;
                    }
               
                    /* Re-init if required */
                    if (!reinit_attempted && !stat->ready && 
                        stat->processed_entries == 0 && stat->commit_step == 0)
                    {
                        /* Prompt the user */
                        reinit_attempted = true;
                        static const char *lines[]={
                            ID2P(LANG_TAGCACHE_BUSY), ID2P(LANG_TAGCACHE_FORCE_UPDATE)};
                        static const struct text_message message={lines, 2};
                        if(gui_syncyesno_run(&message, NULL, NULL) == YESNO_NO)
                            break;
                        int i;
                        FOR_NB_SCREENS(i)
                            screens[i].clear_display();

                        /* Start initialisation */
                        tagcache_rebuild();
                    }

                    /* Display building progress */
                    static long talked_tick = 0;
                    if(global_settings.talk_menu &&
                       (talked_tick == 0
                        || TIME_AFTER(current_tick, talked_tick+7*HZ)))
                    {
                        talked_tick = current_tick;
                        if (stat->commit_step > 0)
                        {
                            talk_id(LANG_TAGCACHE_INIT, false);
                            talk_number(stat->commit_step, true);
                            talk_id(VOICE_OF, true);
                            talk_number(tagcache_get_max_commit_step(), true);
                        } else if(stat->processed_entries)
                        {
                            talk_number(stat->processed_entries, false);
                            talk_id(LANG_BUILDING_DATABASE, true);
                        }
                    }
                    if (stat->commit_step > 0)
                    {
                        if (lang_is_rtl())
                        {
                            splashf(0, "[%d/%d] %s", stat->commit_step,
                                tagcache_get_max_commit_step(),
                                str(LANG_TAGCACHE_INIT));
                        }
                        else
                        {
                            splashf(0, "%s [%d/%d]", str(LANG_TAGCACHE_INIT),
                                stat->commit_step,
                                tagcache_get_max_commit_step());
                        }
                    }
                    else
                    {
                        splashf(0, str(LANG_BUILDING_DATABASE),
                                   stat->processed_entries);
                    }
                }
            }
            if (!tagcache_is_usable())
                return GO_TO_PREVIOUS;
            filter = SHOW_ID3DB;
            tc->dirlevel = last_db_dirlevel;
            tc->selected_item = last_db_selection;
        break;
#endif
        case GO_TO_BROWSEPLUGINS:
            filter = SHOW_PLUGINS;
            strlcpy(folder, PLUGIN_DIR, MAX_PATH);
        break;
    }
    ret_val = rockbox_browse(folder, filter);
    switch ((intptr_t)param)
    {
        case GO_TO_FILEBROWSER:
            if (!get_current_file(last_folder, MAX_PATH) ||
                (!strchr(&last_folder[1], '/') &&
                 global_settings.start_directory[1] != '\0'))
            {
                last_folder[0] = '/';
                last_folder[1] = '\0';
            }
        break;
#ifdef HAVE_TAGCACHE
        case GO_TO_DBBROWSER:
            last_db_dirlevel = tc->dirlevel;
            last_db_selection = tc->selected_item;
        break;
#endif
    }
    return ret_val;
}  
Ejemplo n.º 17
0
bool alarm_screen(void)
{
    int h, m;
    bool done = false;
    struct tm *tm;
    int togo;
    int button;
    bool update = true;
    bool hour_wrapped = false;
    struct viewport vp[NB_SCREENS];

    rtc_get_alarm(&h, &m);

    /* After a battery change the RTC values are out of range */
    if (m > 60 || h > 24) {
        m = 0;
        h = 12;
    } else {
        m = m / 5 * 5; /* 5 min accuracy should be enough */
    }
    FOR_NB_SCREENS(i)
    {
        viewport_set_defaults(&vp[i], i);
    }

    while(!done) {
        if(update)
        {
            FOR_NB_SCREENS(i)
            {
                screens[i].set_viewport(&vp[i]);
                screens[i].clear_viewport();
                screens[i].puts(0, 4, str(LANG_ALARM_MOD_KEYS));
            }
            /* Talk when entering the wakeup screen */
            speak_time(h, m, true, true);
            update = false;
        }

        FOR_NB_SCREENS(i)
        {
            screens[i].set_viewport(&vp[i]);
            screens[i].putsf(0, 1, str(LANG_ALARM_MOD_TIME));
            screens[i].putsf(0, 2, "%02d:%02d", h, m);
            screens[i].update_viewport();
            screens[i].set_viewport(NULL);
        }
        button = get_action(CONTEXT_SETTINGS,HZ);

        switch(button) {
            case ACTION_STD_OK:
            /* prevent that an alarm occurs in the shutdown procedure */
            /* accept alarms only if they are in 2 minutes or more */
            tm = get_time();
            togo = (m + h * 60 - tm->tm_min - tm->tm_hour * 60 + 1440) % 1440;

            if (togo > 1) {
                rtc_init();
                rtc_set_alarm(h,m);
                rtc_enable_alarm(true);
                if (global_settings.talk_menu)
                {
                    talk_id(LANG_ALARM_MOD_TIME_TO_GO, true);
                    talk_value(togo / 60, UNIT_HOUR, true);
                    talk_value(togo % 60, UNIT_MIN, true);
                    talk_force_enqueue_next();
                }
                splashf(HZ*2, str(LANG_ALARM_MOD_TIME_TO_GO),
                               togo / 60, togo % 60);
                done = true;
            } else {
                splash(HZ, ID2P(LANG_ALARM_MOD_ERROR));
                update = true;
            }
            break;

         /* inc(m) */
        case ACTION_SETTINGS_INC:
        case ACTION_SETTINGS_INCREPEAT:
            m += 5;
            if (m == 60) {
                h += 1;
                m = 0;
                hour_wrapped = true;
            }
            if (h == 24)
                h = 0;

            speak_time(h, m, hour_wrapped, false);
            break;

         /* dec(m) */
        case ACTION_SETTINGS_DEC:
        case ACTION_SETTINGS_DECREPEAT:
             m -= 5;
             if (m == -5) {
                 h -= 1;
                 m = 55;
                 hour_wrapped = true;
             }
             if (h == -1)
                 h = 23;

             speak_time(h, m, hour_wrapped, false);
             break;

         /* inc(h) */
         case ACTION_STD_NEXT:
         case ACTION_STD_NEXTREPEAT:
             h = (h+1) % 24;

             if (global_settings.talk_menu)
                 talk_value(h, UNIT_HOUR, false);
             break;

         /* dec(h) */
        case ACTION_STD_PREV:
        case ACTION_STD_PREVREPEAT:
             h = (h+23) % 24;
             
             if (global_settings.talk_menu)
                 talk_value(h, UNIT_HOUR, false);
             break;

        case ACTION_STD_CANCEL:
            rtc_enable_alarm(false);
            splash(HZ*2, ID2P(LANG_ALARM_MOD_DISABLE));
            done = true;
            break;

        case ACTION_NONE:
            hour_wrapped = false;
            break;

        default:
            if(default_event_handler(button) == SYS_USB_CONNECTED)
            {
                rtc_enable_alarm(false);
                return true;
            }
            break;
        }
    }
    return false;
}
Ejemplo n.º 18
0
static int tree_voice_cb(int selected_item, void * data)
{
    struct tree_context * local_tc=(struct tree_context *)data;
    char *name;
    int attr=0;
#ifdef HAVE_TAGCACHE
    bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB;
    char buf[AVERAGE_FILENAME_LENGTH*2];

    if (id3db)
    {
        attr = tagtree_get_attr(local_tc);
        name = tagtree_get_entry_name(local_tc, selected_item, buf, sizeof(buf));
    }
    else
#endif
    {
        struct entry* e = tree_get_entry_at(local_tc, selected_item);
        name = e->name;
        attr = e->attr;
    }
    bool is_dir = (attr & ATTR_DIRECTORY);
    bool did_clip = false;
    /* First the .talk clip case */
    if(is_dir)
    {
        if(global_settings.talk_dir_clip)
        {
            did_clip = true;
            if(ft_play_dirname(name) <0)
                /* failed, not existing */
                did_clip = false;
        }
    } else { /* it's a file */
        if (global_settings.talk_file_clip && (attr & FILE_ATTR_THUMBNAIL))
        {
            did_clip = true;
            ft_play_filename(local_tc->currdir, name);
        }
    }
    if(!did_clip)
    {
        /* say the number or spell if required or as a fallback */
        switch (is_dir ? global_settings.talk_dir : global_settings.talk_file)
        {
        case 1: /* as numbers */
            talk_id(is_dir ? VOICE_DIR : VOICE_FILE, false);
            talk_number(selected_item+1        - (is_dir ? 0 : local_tc->dirsindir),
                        true);
            if(global_settings.talk_filetype
               && !is_dir && *local_tc->dirfilter < NUM_FILTER_MODES)
                say_filetype(attr);
            break;
        case 2: /* spelled */
            talk_shutup();
            if(global_settings.talk_filetype)
            {
                if(is_dir)
                    talk_id(VOICE_DIR, true);
                else if(*local_tc->dirfilter < NUM_FILTER_MODES)
                    say_filetype(attr);
            }
            talk_spell(name, true);
            break;
        }
    }
    return 0;
}
Ejemplo n.º 19
0
static bool clean_shutdown(void (*callback)(void *), void *parameter)
{
#ifdef SIMULATOR
    (void)callback;
    (void)parameter;
    bookmark_autobookmark();
    call_storage_idle_notifys(true);
    exit(0);
#else
    long msg_id = -1;
    int i;

    scrobbler_poweroff();

#if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
    if(!charger_inserted())
#endif
    {
        bool batt_safe = battery_level_safe();
        int audio_stat = audio_status();

        FOR_NB_SCREENS(i)
        {
            screens[i].clear_display();
            screens[i].update();
        }

        if (batt_safe)
        {
#ifdef HAVE_TAGCACHE
            if (!tagcache_prepare_shutdown())
            {
                cancel_shutdown();
                splash(HZ, ID2P(LANG_TAGCACHE_BUSY));
                return false;
            }
#endif
            if (battery_level() > 10)
                splash(0, str(LANG_SHUTTINGDOWN));
            else
            {
                msg_id = LANG_WARNING_BATTERY_LOW;
                splashf(0, "%s %s", str(LANG_WARNING_BATTERY_LOW),
                                    str(LANG_SHUTTINGDOWN));
            }
        }
        else
        {
            msg_id = LANG_WARNING_BATTERY_EMPTY;
            splashf(0, "%s %s", str(LANG_WARNING_BATTERY_EMPTY),
                                str(LANG_SHUTTINGDOWN));
        }

        if (global_settings.fade_on_stop
            && (audio_stat & AUDIO_STATUS_PLAY))
        {
            fade(false, false);
        }

        if (batt_safe) /* do not save on critical battery */
        {
#if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC
            if (audio_stat & AUDIO_STATUS_RECORD)
            {
                rec_command(RECORDING_CMD_STOP);
                /* wait for stop to complete */
                while (audio_status() & AUDIO_STATUS_RECORD)
                    sleep(1);
            }
#endif
            bookmark_autobookmark();

            /* audio_stop_recording == audio_stop for HWCODEC */
            audio_stop();

            if (callback != NULL)
                callback(parameter);

#if CONFIG_CODEC != SWCODEC
            /* wait for audio_stop or audio_stop_recording to complete */
            while (audio_status())
                sleep(1);
#endif

#if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC
            audio_close_recording();
#endif

            if(global_settings.talk_menu)
            {
                bool enqueue = false;
                if(msg_id != -1)
                {
                    talk_id(msg_id, enqueue);
                    enqueue = true;
                }
                talk_id(LANG_SHUTTINGDOWN, enqueue);
#if CONFIG_CODEC == SWCODEC
                voice_wait();
#endif
            }

            system_flush();
#ifdef HAVE_EEPROM_SETTINGS
            if (firmware_settings.initialized)
            {
                firmware_settings.disk_clean = true;
                firmware_settings.bl_version = 0;
                eeprom_settings_store();
            }
#endif
        }
#ifdef HAVE_DIRCACHE
        else
            dircache_disable();
#endif

        shutdown_hw();
    }