From e7461b36092611cca29697f7aca59f2247923d90 Mon Sep 17 00:00:00 2001 From: Miika Pekkarinen Date: Sun, 6 Nov 2005 16:40:20 +0000 Subject: [PATCH] iRiver: New crossfader with more configuration capability. Might still have small bugs, but those will be fixed as soon as possible. Config block version bumped; please SAVE YOUR SETTINGS. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7765 a1c6a512-1295-4272-9138-f99709370657 --- apps/lang/english.lang | 42 +++++++- apps/lang/finnish.lang | 44 ++++++++- apps/pcmbuf.c | 216 +++++++++++++++++++++++++++++++++-------- apps/pcmbuf.h | 2 +- apps/playback.c | 19 ++-- apps/settings.c | 13 +-- apps/settings.h | 13 ++- apps/settings_menu.c | 155 +++++++++++++++++++++-------- 8 files changed, 396 insertions(+), 108 deletions(-) diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 7906ed39c3..9d547173c7 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -3168,9 +3168,9 @@ voice: "" new: id: LANG_CROSSFADE_DURATION -desc: in playback settings -eng: "Crossfade duration" -voice: "Crossfade duration" +desc: DEPRECATED +eng: "" +voice: "" new: id: LANG_MIX @@ -3316,3 +3316,39 @@ desc: when booting up and rebuilding the cache eng: "Scanning disk..." voice: "" new: + +id: LANG_CROSSFADE_ENABLE +desc: in crossfade settings menu +eng: "Enable crossfade" +voice: "Enable crossfade" +new: + +id: LANG_CROSSFADE_FADE_IN_DELAY +desc: in crossfade settings menu +eng: "Fade in delay" +voice: "Fade in delay" +new: + +id: LANG_CROSSFADE_FADE_OUT_DELAY +desc: in crossfade settings menu +eng: "Fade out delay" +voice: "Fade out delay" +new: + +id: LANG_CROSSFADE_FADE_IN_DURATION +desc: in crossfade settings menu +eng: "Fade in duration" +voice: "Fade in duration" +new: + +id: LANG_CROSSFADE_FADE_OUT_DURATION +desc: in crossfade settings menu +eng: "Fade out duration" +voice: "Fade out duration" +new: + +id: LANG_CROSSFADE_FADE_OUT_MODE +desc: in crossfade settings menu +eng: "Fade out mode" +voice: "Fade out mode" +new: diff --git a/apps/lang/finnish.lang b/apps/lang/finnish.lang index 1e6a7eb52e..290d3316d4 100644 --- a/apps/lang/finnish.lang +++ b/apps/lang/finnish.lang @@ -3144,10 +3144,10 @@ voice: "Crossfade" new: "Ristivaihto" id: LANG_CROSSFADE_DURATION -desc: in playback settings -eng: "Crossfade duration" -voice: "Ristivaihdon kesto" -new: "Ristivaihdon kesto" +desc: DEPRECATED +eng: "" +voice: "" +new: "" id: LANG_SHUFFLE_PLAYLIST desc: in playlist menu, reshuffles the order in which songs are played @@ -3347,3 +3347,39 @@ desc: when booting up and rebuilding the cache eng: "Scanning disk..." voice: "" new: "Ladataan hakemistopuu..." + +id: LANG_CROSSFADE_ENABLE +desc: in crossfade settings menu +eng: "Enable crossfade" +voice: "Aktivoi ristivaihto" +new: "Aktivoi ristivaihto" + +id: LANG_CROSSFADE_FADE_IN_DELAY +desc: in crossfade settings menu +eng: "Fade in delay" +voice: "Sisäänhäivytyksen viive" +new: "Sisäänhäivytyksen viive" + +id: LANG_CROSSFADE_FADE_OUT_DELAY +desc: in crossfade settings menu +eng: "Fade out delay" +voice: "Poishäivytyksen viive" +new: "Poishäivytyksen viive" + +id: LANG_CROSSFADE_FADE_IN_DURATION +desc: in crossfade settings menu +eng: "Fade in duration" +voice: "Sisäänhäivytyksen kesto" +new: "Sisäänhäivytyksen kesto" + +id: LANG_CROSSFADE_FADE_OUT_DURATION +desc: in crossfade settings menu +eng: "Fade out duration" +voice: "Poishäivytyksen kesto" +new: "Poishäivytyksen kesto" + +id: LANG_CROSSFADE_FADE_OUT_MODE +desc: in crossfade settings menu +eng: "Fade out mode" +voice: "Poishäivytyksen tyyli" +new: "Poishäivytyksen tyyli" diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index 8ab4ffaae1..6024756c74 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c @@ -38,7 +38,7 @@ #define CHUNK_SIZE PCMBUF_GUARD /* Must be a power of 2 */ -#define NUM_PCM_BUFFERS 64 +#define NUM_PCM_BUFFERS 128 #define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1) #define PCMBUF_WATERMARK (CHUNK_SIZE * 6) @@ -58,7 +58,6 @@ static bool crossfade_enabled; static bool crossfade_active; static bool crossfade_init; static int crossfade_pos; -static int crossfade_amount; static int crossfade_rem; /* Crossfade modes. If CFM_CROSSFADE is selected, normal @@ -71,6 +70,9 @@ enum { CFM_FLUSH }; +static int crossfade_fade_in_amount; +static int crossfade_fade_in_rem; + /* Structure we can use to queue pcm chunks in memory to be played * by the driver code. */ struct pcmbufdesc @@ -225,7 +227,7 @@ bool pcmbuf_is_lowdata(void) return false; } -bool pcmbuf_crossfade_init(int type) +bool pcmbuf_crossfade_init(void) { if (pcmbuf_size - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled || crossfade_active || crossfade_init) { @@ -235,17 +237,8 @@ bool pcmbuf_crossfade_init(int type) logf("pcmbuf_crossfade_init"); pcmbuf_boost(true); - switch (type) { - case CROSSFADE_MODE_CROSSFADE: - crossfade_mode = CFM_CROSSFADE; - break; - case CROSSFADE_MODE_MIX: - crossfade_mode = CFM_MIX; - break; - default: - return false; - } - + crossfade_mode = global_settings.crossfade_fade_out_mixmode + ? CFM_MIX : CFM_CROSSFADE; crossfade_init = true; return true; @@ -308,6 +301,9 @@ void pcmbuf_play_start(void) pcm_play_data(pcmbuf_callback); } +/** + * Commit samples waiting to the pcm buffer. + */ void pcmbuf_flush_fillpos(void) { int copy_n; @@ -335,9 +331,59 @@ void pcmbuf_flush_fillpos(void) } } +/** + * Completely process the crossfade fade out effect with current pcm buffer. + */ +static void crossfade_process_buffer( + int fade_in_delay, int fade_out_delay, int fade_out_rem) +{ + int amount; + int pos; + short *buf; + + /* Fade out the entire current buffer according to settings. */ + amount = fade_out_rem; + pos = crossfade_pos + fade_out_delay*2; + + while (fade_out_rem > 0 && crossfade_mode == CFM_CROSSFADE) + { + int blocksize = MIN(8192, fade_out_rem); + int factor = (fade_out_rem<<8)/amount; + + /* Prevent pcmbuffer from wrapping. */ + if (pos >= pcmbuf_size) + pos -= pcmbuf_size; + blocksize = MIN(pcmbuf_size - pos, blocksize); + buf = (short *)&audiobuffer[pos]; + + fade_out_rem -= blocksize; + pos += blocksize * 2; + while (blocksize > 0) + { + *buf = (*buf * factor) >> 8; + *buf++; + blocksize--; + } + //yield(); + } + + /* And finally set the mixing position where we should start fading in. */ + crossfade_rem -= fade_in_delay; + crossfade_pos += fade_in_delay*2; + if (crossfade_pos >= pcmbuf_size) + crossfade_pos -= pcmbuf_size; + logf("process done!"); +} + +/** + * Initializes crossfader, calculates all necessary parameters and + * performs fade-out with the pcm buffer. + */ static void crossfade_start(void) { int bytesleft = pcmbuf_unplayed_bytes; + int fade_out_rem = 0, fade_out_delay = 0; + int fade_in_delay = 0; crossfade_init = 0; if (bytesleft < CHUNK_SIZE * 4) { @@ -356,44 +402,125 @@ static void crossfade_start(void) switch (crossfade_mode) { case CFM_MIX: case CFM_CROSSFADE: - crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2; - crossfade_rem = crossfade_amount; + /* Initialize the crossfade buffer size. */ + crossfade_rem = (bytesleft - (CHUNK_SIZE * 2))/2; + + /* Get fade out delay from settings. */ + fade_out_delay = NATIVE_FREQUENCY + * global_settings.crossfade_fade_out_delay * 2; + + /* Get fade out duration from settings. */ + fade_out_rem = NATIVE_FREQUENCY + * global_settings.crossfade_fade_out_duration * 2; + + /* Truncate fade out duration if necessary. */ + if (fade_out_rem > crossfade_rem) + fade_out_rem = crossfade_rem; + + /* We want only to modify the last part of the buffer. */ + if (crossfade_rem > fade_out_rem + fade_out_delay) + crossfade_rem = fade_out_rem + fade_out_delay; + + /* Get also fade in duration and delays from settings. */ + crossfade_fade_in_rem = NATIVE_FREQUENCY + * global_settings.crossfade_fade_in_duration * 2; + crossfade_fade_in_amount = crossfade_fade_in_rem; + + /* We should avoid to divide by zero. */ + if (crossfade_fade_in_amount == 0) + crossfade_fade_in_amount = 1; + + fade_in_delay = NATIVE_FREQUENCY + * global_settings.crossfade_fade_in_delay * 2; + + /* Decrease the fade out delay if necessary. */ + fade_out_delay += MIN(crossfade_rem - + fade_out_rem - + fade_out_delay, 0); + if (fade_out_delay < 0) + fade_out_delay = 0; break ; case CFM_FLUSH: - crossfade_amount = bytesleft /2; - crossfade_rem = crossfade_amount; + crossfade_rem = bytesleft /2; break ; } - crossfade_pos -= crossfade_amount*2; + crossfade_pos -= crossfade_rem*2; if (crossfade_pos < 0) crossfade_pos += pcmbuf_size; + + if (crossfade_mode != CFM_FLUSH) { + /* Process the fade out part of the crossfade. */ + crossfade_process_buffer(fade_in_delay, fade_out_delay, fade_out_rem); + } + } +/** + * Fades in samples passed to the function and inserts them + * to the pcm buffer. + */ +static void fade_insert(const short *inbuf, int length) +{ + int copy_n; + int factor; + int i, samples; + short *buf; + + factor = ((crossfade_fade_in_amount-crossfade_fade_in_rem) + <<8)/crossfade_fade_in_amount; + + while (audiobuffer_free < length + audiobuffer_fillpos + + CHUNK_SIZE) + { + pcmbuf_boost(false); + sleep(1); + } + + while (length > 0) { + copy_n = MIN(length, pcmbuf_size - audiobuffer_pos - + audiobuffer_fillpos); + copy_n = MIN(CHUNK_SIZE - audiobuffer_fillpos, copy_n); + + buf = (short *)&audiobuffer[audiobuffer_pos+audiobuffer_fillpos]; + samples = copy_n / 2; + for (i = 0; i < samples; i++) + buf[i] = (inbuf[i] * factor) >> 8; + + inbuf += samples; + audiobuffer_fillpos += copy_n; + length -= copy_n; + + /* Pre-buffer to meet CHUNK_SIZE requirement */ + if (audiobuffer_fillpos < CHUNK_SIZE && length == 0) { + break ; + } + + pcmbuf_flush_fillpos(); + } +} + +/** + * Fades in buf2 and mixes it with buf. + */ static __inline int crossfade(short *buf, const short *buf2, int length) { int size, i; - int val1, val2; + int size_insert = 0; + int factor; - size = MIN(length, crossfade_rem); + size = MAX(0, MIN(length, crossfade_rem)); switch (crossfade_mode) { - /* Mix two streams. */ + /* Fade in the current stream and mix it. */ case CFM_MIX: - /* Bias & add & clip. */ - for (i = 0; i < size; i++) { - buf[i] = MIN(MAX(buf[i] + buf2[i], -32768), 32767); - } - break ; - - /* Fade two streams. */ case CFM_CROSSFADE: - val1 = (crossfade_rem<<10)/crossfade_amount; - val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount; + factor = ((crossfade_fade_in_amount-crossfade_fade_in_rem) + <<8)/crossfade_fade_in_amount; for (i = 0; i < size; i++) { - buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10; + buf[i] = MIN(MAX(buf[i] + ((buf2[i] * factor) >> 8), -32768), 32767); } break ; @@ -405,12 +532,20 @@ int crossfade(short *buf, const short *buf2, int length) //memcpy((char *)buf, (char *)buf2, size*2); break ; } - + + crossfade_fade_in_rem = MAX(0, crossfade_fade_in_rem - size); crossfade_rem -= size; if (crossfade_rem <= 0) - crossfade_active = false; - - return size; + { + size_insert = MAX(0, MIN(crossfade_fade_in_rem, length - size)); + fade_insert(&buf2[size], size_insert*2); + crossfade_fade_in_rem -= size_insert; + + if (crossfade_fade_in_rem <= 0) + crossfade_active = false; + } + + return size + size_insert; } static bool prepare_insert(long length) @@ -486,13 +621,12 @@ void pcmbuf_flush_buffer(long length) } while (length > 0) { + pcmbuf_flush_fillpos(); copy_n = MIN(length, pcmbuf_size - audiobuffer_pos); memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n); audiobuffer_fillpos = copy_n; buf += copy_n; length -= copy_n; - if (length > 0) - pcmbuf_flush_fillpos(); } } @@ -537,13 +671,12 @@ bool pcmbuf_insert_buffer(char *buf, long length) } while (length > 0) { + pcmbuf_flush_fillpos(); copy_n = MIN(length, pcmbuf_size - audiobuffer_pos); memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n); audiobuffer_fillpos = copy_n; buf += copy_n; length -= copy_n; - if (length > 0) - pcmbuf_flush_fillpos(); } } @@ -674,6 +807,9 @@ void pcmbuf_crossfade_enable(bool on_off) bool pcmbuf_is_crossfade_enabled(void) { + if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE) + return global_settings.playlist_shuffle; + return crossfade_enabled; } diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h index 629f969e7d..9031db60b1 100644 --- a/apps/pcmbuf.h +++ b/apps/pcmbuf.h @@ -43,7 +43,7 @@ void pcmbuf_set_boost_mode(bool state); bool pcmbuf_is_lowdata(void); void pcmbuf_flush_audio(void); void pcmbuf_play_start(void); -bool pcmbuf_crossfade_init(int type); +bool pcmbuf_crossfade_init(void); void pcmbuf_add_event(void (*event_handler)(void)); unsigned int pcmbuf_get_latency(void); bool pcmbuf_insert_buffer(char *buf, long length); diff --git a/apps/playback.c b/apps/playback.c index 8d869ceda3..61d0d62af8 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -1441,8 +1441,7 @@ void audio_update_trackinfo(void) cur_ti->start_pos = 0; ci.taginfo_ready = (bool *)&cur_ti->taginfo_ready; if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()) { - pcmbuf_crossfade_init(new_track ? CROSSFADE_MODE_CROSSFADE - : global_settings.crossfade); + pcmbuf_crossfade_init(); codec_track_changed(); } else { pcmbuf_add_event(codec_track_changed); @@ -1706,7 +1705,7 @@ void audio_thread(void) ci.stop_codec = true; ci.reload_codec = false; ci.seek_time = 0; - pcmbuf_crossfade_init(CROSSFADE_MODE_CROSSFADE); + pcmbuf_crossfade_init(); while (audio_codec_loaded) yield(); audio_play_start((int)ev.data); @@ -2227,13 +2226,12 @@ void audio_set_buffer_margin(int setting) } /* Set crossfade & PCM buffer length. */ -void audio_set_crossfade(int type) +void audio_set_crossfade(int enable) { long size; bool was_playing = playing; int offset = 0; - static const int lookup[] = {1, 2, 4, 6, 8, 10, 12, 14}; - int seconds = lookup[global_settings.crossfade_duration]; + int seconds = 1; if (!filebuf) return; /* Audio buffers not yet set up */ @@ -2242,8 +2240,11 @@ void audio_set_crossfade(int type) if (playing) offset = cur_ti->id3.offset; - if (type == CROSSFADE_MODE_OFF) - seconds = 1; + if (enable) + { + seconds = global_settings.crossfade_fade_out_delay + + global_settings.crossfade_fade_out_duration; + } /* Buffer has to be at least 2s long. */ seconds += 2; @@ -2259,7 +2260,7 @@ void audio_set_crossfade(int type) if (was_playing) splash(0, true, str(LANG_RESTARTING_PLAYBACK)); pcmbuf_init(size); - pcmbuf_crossfade_enable(type != CROSSFADE_MODE_OFF); + pcmbuf_crossfade_enable(enable); reset_buffer(); logf("abuf:%dB", pcmbuf_get_bufsize()); logf("fbuf:%dB", filebuflen); diff --git a/apps/settings.c b/apps/settings.c index 870376c8b2..5eb434b31d 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -85,7 +85,7 @@ const char rec_base_directory[] = REC_BASE_DIR; #include "dsp.h" #endif -#define CONFIG_BLOCK_VERSION 28 +#define CONFIG_BLOCK_VERSION 29 #define CONFIG_BLOCK_SIZE 512 #define RTC_BLOCK_SIZE 44 @@ -417,10 +417,6 @@ static const struct bit_entry hd_bits[] = {4, S_O(rec_trigger_mode ), 0, "trigger mode", "off,once,repeat"}, #endif -#if CONFIG_CODEC == SWCODEC - {3, S_O(crossfade_duration), 0, "crossfade duration", "1s,2s,4s,6s,8s,10s,12s,14s"}, -#endif - #if CONFIG_BACKLIGHT == BL_IRIVER /* backlight fading */ {2, S_O(backlight_fade_in), 1, "backlight fade in", "off,500ms,1s,2s"}, @@ -436,13 +432,18 @@ static const struct bit_entry hd_bits[] = {1, S_O(runtimedb), false, "gather runtime data", off_on }, #if CONFIG_CODEC == SWCODEC - {2, S_O(crossfade), 0, "crossfade type", "off,crossfade,mix"}, {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,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"}, #endif #ifdef HAVE_DIRCACHE {1, S_O(dircache), false, "dircache", off_on }, diff --git a/apps/settings.h b/apps/settings.h index 99254e377f..63349c646e 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -109,9 +109,8 @@ #define TRIG_DURATION_COUNT 13 extern const char * const trig_durations[TRIG_DURATION_COUNT]; -#define CROSSFADE_MODE_OFF 0 -#define CROSSFADE_MODE_CROSSFADE 1 -#define CROSSFADE_MODE_MIX 2 +#define CROSSFADE_ENABLE_SHUFFLE 1 +#define CROSSFADE_ENABLE_ALWAYS 2 /* These define "virtual pointers", which could either be a literal string, or a mean a string ID if the pointer is in a certain range. @@ -157,8 +156,12 @@ struct user_settings bool superbass; /* true/false */ #if CONFIG_CODEC == SWCODEC - int crossfade; - int crossfade_duration; + int crossfade; /* Enable crossfade (0=off,1=shuffle,2=always) */ + int crossfade_fade_in_delay; /* Fade in delay (0-15s) */ + int crossfade_fade_out_delay; /* Fade out delay (0-15s) */ + int crossfade_fade_in_duration; /* Fade in duration (0-15s) */ + int crossfade_fade_out_duration; /* Fade out duration (0-15s) */ + int crossfade_fade_out_mixmode; /* Fade out mode (0=crossfade,1=mix) */ #endif int rec_quality; /* 0-7 */ diff --git a/apps/settings_menu.c b/apps/settings_menu.c index b6d6b2bdac..65e40f9f10 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -1110,44 +1110,6 @@ static bool id3_order(void) mpeg_id3_options); } -#if CONFIG_CODEC == SWCODEC -static bool crossfade(void) -{ - static const struct opt_items names[] = { - { STR(LANG_OFF) }, - { STR(LANG_CROSSFADE) }, - { STR(LANG_MIX) }, - }; - bool ret; - - ret = set_option( str(LANG_CROSSFADE), - &global_settings.crossfade, INT, names, 3, NULL); - audio_set_crossfade(global_settings.crossfade); - - return ret; -} - -static bool crossfade_duration(void) -{ - static const struct opt_items names[] = { - { "1s", TALK_ID(1, UNIT_SEC) }, - { "2s", TALK_ID(2, UNIT_SEC) }, - { "4s", TALK_ID(4, UNIT_SEC) }, - { "6s", TALK_ID(6, UNIT_SEC) }, - { "8s", TALK_ID(8, UNIT_SEC) }, - { "10s", TALK_ID(10, UNIT_SEC) }, - { "12s", TALK_ID(12, UNIT_SEC) }, - { "14s", TALK_ID(14, UNIT_SEC) }, - }; - bool ret; - ret=set_option( str(LANG_CROSSFADE_DURATION), - &global_settings.crossfade_duration, INT, names, 8, NULL); - audio_set_crossfade(global_settings.crossfade); - - return ret; -} -#endif - static bool next_folder(void) { return set_bool( str(LANG_NEXT_FOLDER), &global_settings.next_folder ); @@ -1239,6 +1201,120 @@ static bool replaygain_settings_menu(void) return result; } +static bool crossfade(void) +{ + static const struct opt_items names[] = { + { STR(LANG_OFF) }, + { STR(LANG_SHUFFLE) }, + { STR(LANG_ALWAYS) }, + }; + + bool ret; + + ret=set_option( str(LANG_CROSSFADE_ENABLE), + &global_settings.crossfade, INT, names, 3, NULL); + + audio_set_crossfade(global_settings.crossfade); + + return ret; +} + +static const struct opt_items crossfade_time[] = { + { "0s", TALK_ID(0, UNIT_SEC) }, + { "1s", TALK_ID(1, UNIT_SEC) }, + { "2s", TALK_ID(2, UNIT_SEC) }, + { "3s", TALK_ID(3, UNIT_SEC) }, + { "4s", TALK_ID(4, UNIT_SEC) }, + { "5s", TALK_ID(5, UNIT_SEC) }, + { "6s", TALK_ID(6, UNIT_SEC) }, + { "7s", TALK_ID(7, UNIT_SEC) }, + { "8s", TALK_ID(8, UNIT_SEC) }, + { "9s", TALK_ID(9, UNIT_SEC) }, + { "10s", TALK_ID(10, UNIT_SEC) }, + { "11s", TALK_ID(11, UNIT_SEC) }, + { "12s", TALK_ID(12, UNIT_SEC) }, + { "13s", TALK_ID(13, UNIT_SEC) }, + { "14s", TALK_ID(14, UNIT_SEC) }, + { "15s", TALK_ID(15, UNIT_SEC) }, +}; + +static bool crossfade_fade_in_delay(void) +{ + bool ret; + ret=set_option( str(LANG_CROSSFADE_FADE_IN_DELAY), + &global_settings.crossfade_fade_in_delay, INT, crossfade_time, 8, NULL); + audio_set_crossfade(global_settings.crossfade); + + return ret; +} + +static bool crossfade_fade_out_delay(void) +{ + bool ret; + ret=set_option( str(LANG_CROSSFADE_FADE_OUT_DELAY), + &global_settings.crossfade_fade_out_delay, INT, crossfade_time, 8, NULL); + audio_set_crossfade(global_settings.crossfade); + + return ret; +} + +static bool crossfade_fade_in_duration(void) +{ + bool ret; + ret=set_option( str(LANG_CROSSFADE_FADE_IN_DURATION), + &global_settings.crossfade_fade_in_duration, INT, crossfade_time, 16, NULL); + audio_set_crossfade(global_settings.crossfade); + + return ret; +} + +static bool crossfade_fade_out_duration(void) +{ + bool ret; + ret=set_option( str(LANG_CROSSFADE_FADE_OUT_DURATION), + &global_settings.crossfade_fade_out_duration, INT, crossfade_time, 16, NULL); + audio_set_crossfade(global_settings.crossfade); + + return ret; +} + +static bool crossfade_fade_out_mixmode(void) +{ + static const struct opt_items names[] = { + { STR(LANG_CROSSFADE) }, + { STR(LANG_MIX) }, + }; + bool ret; + ret=set_option( str(LANG_CROSSFADE_FADE_OUT_MODE), + &global_settings.crossfade_fade_out_mixmode, INT, names, 2, NULL); + + return ret; +} + +/** + * Menu to configure the crossfade settings. + */ +static bool crossfade_settings_menu(void) +{ + int m; + bool result; + + static const struct menu_item items[] = { + { ID2P(LANG_CROSSFADE_ENABLE), crossfade }, + { ID2P(LANG_CROSSFADE_FADE_IN_DELAY), crossfade_fade_in_delay }, + { ID2P(LANG_CROSSFADE_FADE_IN_DURATION), crossfade_fade_in_duration }, + { ID2P(LANG_CROSSFADE_FADE_OUT_DELAY), crossfade_fade_out_delay }, + { ID2P(LANG_CROSSFADE_FADE_OUT_DURATION), crossfade_fade_out_duration }, + { ID2P(LANG_CROSSFADE_FADE_OUT_MODE), crossfade_fade_out_mixmode }, + }; + + m=menu_init( items, sizeof(items) / sizeof(*items), NULL, + NULL, NULL, NULL); + result = menu_run(m); + menu_exit(m); + return result; +} + static bool beep(void) { static const struct opt_items names[] = { @@ -1289,8 +1365,7 @@ static bool playback_settings_menu(void) { ID2P(LANG_MP3BUFFER_MARGIN), buffer_margin }, { ID2P(LANG_FADE_ON_STOP), set_fade_on_stop }, #if CONFIG_CODEC == SWCODEC - { ID2P(LANG_CROSSFADE), crossfade }, - { ID2P(LANG_CROSSFADE_DURATION), crossfade_duration }, + { ID2P(LANG_CROSSFADE), crossfade_settings_menu }, { ID2P(LANG_REPLAYGAIN), replaygain_settings_menu }, { ID2P(LANG_BEEP), beep }, #endif