Crossfade (and pcm buffer size) length is now configurable.
Implemented anti-skip buffer setting for iriver also. Settings block
bumped up, PLEASE SAVE YOUR SETTINGS BEFORE UPGRADING.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7210 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 4d3c90c..21e30e9 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -230,6 +230,7 @@
     int button;
     int line;
     bool done = false;
+    int bufsize = pcmbuf_get_bufsize();
 
     ticks = boost_ticks = 0;
 
@@ -253,12 +254,12 @@
         lcd_clear_display();
 
         snprintf(buf, sizeof(buf), "pcm: %d/%d",
-                 PCMBUF_SIZE-(int)audiobuffer_free, PCMBUF_SIZE);
+                 bufsize-(int)audiobuffer_free, bufsize);
         lcd_puts(0, line++, buf);
 
         /* Playable space left */
-        scrollbar(0, line*8, LCD_WIDTH, 6, PCMBUF_SIZE, 0,
-                  PCMBUF_SIZE-audiobuffer_free, HORIZONTAL);
+        scrollbar(0, line*8, LCD_WIDTH, 6, bufsize, 0,
+                  bufsize-audiobuffer_free, HORIZONTAL);
         line++;
 
         snprintf(buf, sizeof(buf), "codec: %d/%d", codecbufused, codecbuflen);
diff --git a/apps/main.c b/apps/main.c
index 182d464..90be703 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -106,6 +106,7 @@
     font_init();
     show_logo();
     lang_init();
+    audio_init();
     settings_reset();
     settings_calc_config_sector();
     settings_load(SETTINGS_ALL);
@@ -127,8 +128,6 @@
               global_settings.mdb_shape,
               global_settings.mdb_enable,
               global_settings.superbass);
-    audio_init();
-    pcmbuf_init();    
     button_clear_queue(); /* Empty the keyboard buffer */
 }
 
@@ -260,6 +259,11 @@
         }
     }
 
+    /* On software codec platforms we have to init audio before
+       calling audio_set_buffer_margin(). */
+#if (CONFIG_HWCODEC == MASNONE)
+    audio_init();
+#endif
     settings_calc_config_sector();
     settings_load(SETTINGS_ALL);
     settings_apply();
@@ -284,10 +288,10 @@
               global_settings.mdb_shape,
               global_settings.mdb_enable,
               global_settings.superbass);
-    audio_init();
 #if (CONFIG_HWCODEC == MASNONE)
-    pcmbuf_init();
     sound_settings_apply();
+#else
+    audio_init();
 #endif
 #if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR)
     pcm_init_recording();
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index bcf971d..d3f4d15 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -32,15 +32,17 @@
 #include "system.h"
 #include <string.h>
 #include "buffer.h"
+#include "settings.h"
+#include "audio.h"
 
-#define CHUNK_SIZE           32768
+#define CHUNK_SIZE           PCMBUF_GUARD
 /* Must be a power of 2 */
-#define NUM_PCM_BUFFERS      (PCMBUF_SIZE / CHUNK_SIZE)
+#define NUM_PCM_BUFFERS      64
 #define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
-#define PCMBUF_WATERMARK     (CHUNK_SIZE * 10)
-#define PCMBUF_CF_WATERMARK  (PCMBUF_SIZE - CHUNK_SIZE*8)
+#define PCMBUF_WATERMARK     (CHUNK_SIZE * 6)
 
 /* Audio buffer related settings. */
+static long pcmbuf_size = 0;      /* Size of the PCM buffer. */
 static char *audiobuffer;
 static long audiobuffer_pos;      /* Current audio buffer write index. */
 long audiobuffer_free;            /* Amount of bytes left in the buffer. */
@@ -85,7 +87,7 @@
 int pcmbuf_unplayed_bytes;
 int pcmbuf_watermark;
 void (*pcmbuf_watermark_event)(int bytes_left);
-//static int last_chunksize;
+static int last_chunksize;
 
 static void pcmbuf_boost(bool state)
 {
@@ -111,6 +113,9 @@
 {
     struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
     
+    pcmbuf_unplayed_bytes -= last_chunksize;
+    audiobuffer_free += last_chunksize;
+        
     if(desc->size == 0)
     {
         /* The buffer is finished, call the callback function */
@@ -124,8 +129,6 @@
     
     if(pcmbuf_num_used_buffers())
     {
-        pcmbuf_unplayed_bytes -= desc->size;
-        audiobuffer_free += desc->size;
     
         *start = desc->addr;
         *size = desc->size;
@@ -142,6 +145,7 @@
             pcmbuf_event_handler();
     }
 
+    last_chunksize = *size;
     if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
     {
         if(pcmbuf_watermark_event)
@@ -199,7 +203,7 @@
     int latency;
     
     /* This has to be done better. */
-    latency = (PCMBUF_SIZE - audiobuffer_free - CHUNK_SIZE)/4 / (44100/1000);
+    latency = (pcmbuf_size - audiobuffer_free - CHUNK_SIZE)/4 / (44100/1000);
     if (latency < 0)
         latency = 0;
     
@@ -211,7 +215,7 @@
     if (!pcm_is_playing() || pcm_is_paused() || crossfade_init || crossfade_active)
         return false;
     
-    if (pcmbuf_unplayed_bytes < PCMBUF_WATERMARK)
+    if (pcmbuf_unplayed_bytes < CHUNK_SIZE * 4)
         return true;
         
     return false;
@@ -219,7 +223,7 @@
 
 bool pcmbuf_crossfade_init(void)
 {
-    if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled
+    if (pcmbuf_size - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled
         || crossfade_active || crossfade_init) {
         pcmbuf_flush_audio();
         return false;
@@ -236,13 +240,13 @@
 void pcmbuf_play_stop(void)
 {
     pcm_play_stop();
-    //last_chunksize = 0;
+    last_chunksize = 0;
     pcmbuf_unplayed_bytes = 0;
     pcmbuf_read_index = 0;
     pcmbuf_write_index = 0;
     audiobuffer_pos = 0;
     audiobuffer_fillpos = 0;
-    audiobuffer_free = PCMBUF_SIZE;
+    audiobuffer_free = pcmbuf_size;
     crossfade_init = false;
     crossfade_active = false;
     
@@ -251,16 +255,22 @@
     
 }
 
-void pcmbuf_init(void)
+void pcmbuf_init(long bufsize)
 {
+    pcmbuf_size = bufsize;
     audiobuffer = &audiobuf[(audiobufend - audiobuf) - 
-                            PCMBUF_SIZE - PCMBUF_GUARD];
-    guardbuf = &audiobuffer[PCMBUF_SIZE];
+                            pcmbuf_size - PCMBUF_GUARD];
+    guardbuf = &audiobuffer[pcmbuf_size];
     pcmbuf_event_handler = NULL;
     pcm_init();
     pcmbuf_play_stop();
 }
 
+long pcmbuf_get_bufsize(void)
+{
+    return pcmbuf_size;
+}
+
 /** Initialize a track switch so that audio playback will not stop but
  *  the switch to next track would happen as soon as possible.
  */
@@ -296,8 +306,8 @@
         }
         pcmbuf_event_handler = NULL;
         audiobuffer_pos += copy_n;
-        if (audiobuffer_pos >= PCMBUF_SIZE)
-            audiobuffer_pos -= PCMBUF_SIZE;
+        if (audiobuffer_pos >= pcmbuf_size)
+            audiobuffer_pos -= pcmbuf_size;
         audiobuffer_free -= copy_n;
         audiobuffer_fillpos -= copy_n;
     }
@@ -334,7 +344,7 @@
     
     crossfade_pos -= crossfade_amount*2;
     if (crossfade_pos < 0)
-        crossfade_pos += PCMBUF_SIZE;
+        crossfade_pos += pcmbuf_size;
 }
 
 static __inline
@@ -383,7 +393,7 @@
     if (!pcm_is_playing()) {
         pcmbuf_boost(true);
         crossfade_active = false;
-        if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*4) {
+        if (audiobuffer_free < pcmbuf_size - CHUNK_SIZE*4) {
             logf("pcm starting");
             pcm_play_data(pcmbuf_callback);
         }
@@ -409,7 +419,7 @@
         *realsize = MIN(length, PCMBUF_GUARD);
         ptr = &guardbuf[0];
     } else {
-        *realsize = MIN(length, PCMBUF_SIZE - audiobuffer_pos
+        *realsize = MIN(length, pcmbuf_size - audiobuffer_pos
                             - audiobuffer_fillpos);
         if (*realsize < length) {
             *realsize += MIN((long)(length - *realsize), PCMBUF_GUARD);
@@ -436,18 +446,18 @@
         buf = &guardbuf[0];
         length = MIN(length, PCMBUF_GUARD);
         while (length > 0 && crossfade_active) {
-            copy_n = MIN(length, PCMBUF_SIZE - crossfade_pos);
+            copy_n = MIN(length, pcmbuf_size - crossfade_pos);
             copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos], 
                     (const short *)buf, copy_n/2);
             buf += copy_n;
             length -= copy_n;
             crossfade_pos += copy_n;
-            if (crossfade_pos >= PCMBUF_SIZE)
-                crossfade_pos -= PCMBUF_SIZE;
+            if (crossfade_pos >= pcmbuf_size)
+                crossfade_pos -= pcmbuf_size;
         }
         
         while (length > 0) {
-            copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos);
+            copy_n = MIN(length, pcmbuf_size - audiobuffer_pos);
             memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n);
             audiobuffer_fillpos = copy_n;
             buf += copy_n;
@@ -460,11 +470,11 @@
     audiobuffer_fillpos += length;
 
     try_flush:
-    if (audiobuffer_fillpos < CHUNK_SIZE && PCMBUF_SIZE
+    if (audiobuffer_fillpos < CHUNK_SIZE && pcmbuf_size
         - audiobuffer_pos - audiobuffer_fillpos > 0)
         return ;
 
-    copy_n = audiobuffer_fillpos - (PCMBUF_SIZE - audiobuffer_pos);
+    copy_n = audiobuffer_fillpos - (pcmbuf_size - audiobuffer_pos);
     if (copy_n > 0) {
         audiobuffer_fillpos -= copy_n;
         pcmbuf_flush_fillpos();
@@ -486,19 +496,19 @@
     
     if (crossfade_active) {
         while (length > 0 && crossfade_active) {
-            copy_n = MIN(length, PCMBUF_SIZE - crossfade_pos);
+            copy_n = MIN(length, pcmbuf_size - crossfade_pos);
                 
             copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos],
                                     (const short *)buf, copy_n/2);
             buf += copy_n;
             length -= copy_n;
             crossfade_pos += copy_n;
-            if (crossfade_pos >= PCMBUF_SIZE)
-                crossfade_pos -= PCMBUF_SIZE;
+            if (crossfade_pos >= pcmbuf_size)
+                crossfade_pos -= pcmbuf_size;
         }
         
         while (length > 0) {
-            copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos);
+            copy_n = MIN(length, pcmbuf_size - audiobuffer_pos);
             memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n);
             audiobuffer_fillpos = copy_n;
             buf += copy_n;
@@ -509,7 +519,7 @@
     }
         
     while (length > 0) {
-        copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos -
+        copy_n = MIN(length, pcmbuf_size - audiobuffer_pos -
                     audiobuffer_fillpos);
         copy_n = MIN(CHUNK_SIZE - audiobuffer_fillpos, copy_n);
 
@@ -533,12 +543,12 @@
 void pcmbuf_crossfade_enable(bool on_off)
 {
     crossfade_enabled = on_off;
-    
+
     if (crossfade_enabled) {
-        pcmbuf_set_watermark(PCMBUF_CF_WATERMARK, pcmbuf_watermark_callback);
+        pcmbuf_set_watermark(pcmbuf_size - (CHUNK_SIZE*6), pcmbuf_watermark_callback);
     } else {
         pcmbuf_set_watermark(PCMBUF_WATERMARK, pcmbuf_watermark_callback);
-    }    
+    }
 }
 
 bool pcmbuf_is_crossfade_enabled(void)
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h
index 2b4023f..29217af 100644
--- a/apps/pcmbuf.h
+++ b/apps/pcmbuf.h
@@ -22,10 +22,8 @@
 /* Guard buffer for crossfader when dsp is enabled. */
 #define PCMBUF_GUARD  32768
 
-/* PCM audio buffer. */
-#define PCMBUF_SIZE   (1*1024*1024)
-
-void pcmbuf_init(void);
+void pcmbuf_init(long bufsize);
+long pcmbuf_get_bufsize(void);
 
 void pcmbuf_play_stop(void);
 bool pcmbuf_is_crossfade_active(void);
diff --git a/apps/playback.c b/apps/playback.c
index 4929cf3..bf8e5bd 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -172,6 +172,7 @@
 static int conf_bufferlimit;
 static int conf_watermark;
 static int conf_filechunk;
+static int buffer_margin;
 
 static bool v1first = false;
 
@@ -468,11 +469,21 @@
     return true;
 }
 
+static void set_filebuf_watermark(int seconds)
+{
+    long bytes;
+
+    bytes = MAX((int)cur_ti->id3.bitrate * seconds * (1000/8), conf_watermark);
+    bytes = MIN(bytes, codecbuflen / 2);
+    conf_watermark = bytes;
+}
+
 void codec_configure_callback(int setting, void *value)
 {
     switch (setting) {
     case CODEC_SET_FILEBUF_WATERMARK:
         conf_watermark = (unsigned int)value;
+        set_filebuf_watermark(buffer_margin);
         break;
         
     case CODEC_SET_FILEBUF_CHUNKSIZE:
@@ -852,6 +863,7 @@
             return false;
         }
     }
+    set_filebuf_watermark(buffer_margin);
     tracks[track_widx].id3.elapsed = 0;
 
     /* Starting playback from an offset is only support in MPA at the moment */
@@ -1754,13 +1766,36 @@
     return pos;    
 }
 
-#ifndef SIMULATOR
-void audio_set_buffer_margin(int seconds)
+void audio_set_buffer_margin(int setting)
 {
-    (void)seconds;
-    logf("bufmargin: %d", seconds);
+    int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600};
+    buffer_margin = lookup[setting];
+    logf("buffer margin: %ds", buffer_margin);
+    set_filebuf_watermark(buffer_margin);
 }
-#endif
+
+void audio_set_crossfade_amount(int seconds)
+{
+    long size;
+
+    /* Playback has to be stopped before changing the buffer size. */
+    audio_stop_playback();
+
+    /* Buffer has to be at least 2s long. */
+    seconds += 2;
+    logf("buf len: %d", seconds);
+    size = seconds * (NATIVE_FREQUENCY*4);
+    if (pcmbuf_get_bufsize() == size)
+        return ;
+
+    /* Re-initialize audio system. */
+    pcmbuf_init(size);
+    pcmbuf_crossfade_enable(seconds > 2);
+    codecbuflen = audiobufend - audiobuf - pcmbuf_get_bufsize()
+                  - PCMBUF_GUARD - MALLOC_BUFSIZE - GUARD_BUFSIZE;
+    logf("abuf:%dB", pcmbuf_get_bufsize());
+    logf("fbuf:%dB", codecbuflen);
+}
 
 void mpeg_id3_options(bool _v1first)
 {
@@ -1786,9 +1821,6 @@
 void audio_init(void)
 {
     logf("audio api init");
-    codecbuflen = audiobufend - audiobuf - PCMBUF_SIZE - PCMBUF_GUARD
-                  - MALLOC_BUFSIZE - GUARD_BUFSIZE;
-    //codecbuflen = 2*512*1024;
     codecbufused = 0;
     filling = false;
     codecbuf = &audiobuf[MALLOC_BUFSIZE];
@@ -1803,10 +1835,6 @@
     /* Just to prevent cur_ti never be anything random. */
     cur_ti = &tracks[0];
     
-    logf("abuf:%0x", PCMBUF_SIZE);
-    logf("fbuf:%0x", codecbuflen);
-    logf("mbuf:%0x", MALLOC_BUFSIZE);
-
     audio_set_track_buffer_event(test_buffer_event);
     audio_set_track_unbuffer_event(test_unbuffer_event);
     
@@ -1833,9 +1861,6 @@
                   codec_thread_name);
     create_thread(audio_thread, audio_stack, sizeof(audio_stack),
                   audio_thread_name);
-#ifndef SIMULATOR
-    audio_is_initialized = true;
-#endif
 }
 
 
diff --git a/apps/settings.c b/apps/settings.c
index 524dfcd..e9f45de 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -77,7 +77,7 @@
 #include "pcm_playback.h"
 #endif
 
-#define CONFIG_BLOCK_VERSION 22
+#define CONFIG_BLOCK_VERSION 23
 #define CONFIG_BLOCK_SIZE 512
 #define RTC_BLOCK_SIZE 44
 
@@ -198,7 +198,7 @@
         "stereo,mono,custom,mono left,mono right,karaoke" },
     {8, S_O(stereo_width), 100, "stereo width", NULL},
     /* playback */
-    {2, S_O(resume), false, "resume", off_on },
+    {1, S_O(resume), false, "resume", off_on },
     {1, S_O(playlist_shuffle), false, "shuffle", off_on },
     {16 | SIGNED, S_O(resume_index), -1, NULL, NULL },
     {16 | SIGNED, S_O(resume_first_index), 0, NULL, NULL },
@@ -301,7 +301,12 @@
     {4, S_O(ff_rewind_min_step), FF_REWIND_1000, 
         "scan min step", "1,2,3,4,5,6,8,10,15,20,25,30,45,60" },
     {4, S_O(ff_rewind_accel), 3, "scan accel", NULL },
+#if CONFIG_HWCODEC == MASNONE
+    {3, S_O(buffer_margin), 0, "antiskip",
+        "5s,15s,30s,1min,2min,3min,5min,10min" },
+#else
     {3, S_O(buffer_margin), 0, "antiskip", NULL },
+#endif
     /* disk */
 #ifndef HAVE_MMC
 #ifdef HAVE_ATA_POWER_OFF
@@ -392,7 +397,7 @@
 #endif
 
 #if CONFIG_HWCODEC == MASNONE
-    {1, S_O(crossfade), false, "crossfade", off_on},
+    {3, S_O(crossfade), 0, "crossfade", "off,2s,4s,6s,8s,10s,12s,14s"},
 #endif
 
 #if CONFIG_BACKLIGHT == BL_IRIVER
@@ -846,7 +851,7 @@
     }
 
 #if CONFIG_HWCODEC == MASNONE
-    pcmbuf_crossfade_enable(global_settings.crossfade);
+    audio_set_crossfade_amount(global_settings.crossfade*2);
 #endif
 
 #ifdef HAVE_SPDIF_POWER
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index a94dda8..490970d 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -917,12 +917,33 @@
                    NULL, 1000, 1000, 20000 );
 }
 
+#if CONFIG_HWCODEC == MASNONE
+static bool buffer_margin(void)
+{
+    int ret;
+    static const struct opt_items names[] = {
+        { "5s", TALK_ID(5, UNIT_SEC) },
+        { "15s", TALK_ID(15, UNIT_SEC) },
+        { "30s", TALK_ID(30, UNIT_SEC) },
+        { "1min", TALK_ID(1, UNIT_MIN) },
+        { "2min", TALK_ID(2, UNIT_MIN) },
+        { "3min", TALK_ID(3, UNIT_MIN) },
+        { "5min", TALK_ID(5, UNIT_MIN) },
+        { "10min", TALK_ID(10, UNIT_MIN) }
+    };
+    
+    ret = set_option(str(LANG_MP3BUFFER_MARGIN), &global_settings.buffer_margin,
+                        INT, names, 8, audio_set_buffer_margin);
+    return ret;
+}
+#else
 static bool buffer_margin(void)
 {
     return set_int(str(LANG_MP3BUFFER_MARGIN), "s", UNIT_SEC,
                    &global_settings.buffer_margin,
                    audio_set_buffer_margin, 1, 0, 7 );
 }
+#endif
 
 static bool ff_rewind_min_step(void)
 { 
@@ -1106,11 +1127,23 @@
 #if CONFIG_HWCODEC == MASNONE
 static bool crossfade(void)
 {
-    bool rc = set_bool( str(LANG_CROSSFADE), &global_settings.crossfade );
-    pcmbuf_crossfade_enable(global_settings.crossfade);
-
-    return rc;
+    static const struct opt_items names[] = {
+        { STR(LANG_OFF) },
+        { "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), &global_settings.crossfade,
+                   INT, names, 8, audio_set_crossfade_amount);
+    
+    return ret;
 }
+
 #endif
 
 static bool next_folder(void)
diff --git a/firmware/mp3_playback.c b/firmware/mp3_playback.c
index 731db17..dfe08e5 100644
--- a/firmware/mp3_playback.c
+++ b/firmware/mp3_playback.c
@@ -621,7 +621,7 @@
     playstart_tick = 0;
     cumulative_ticks = 0;
     callback_for_more = 0;
-    audio_is_initialized = false;
+    audio_is_initialized = true;
 #endif
 }
 void mp3_shutdown(void)
diff --git a/uisimulator/common/stubs.c b/uisimulator/common/stubs.c
index 9643937..446d40c 100644
--- a/uisimulator/common/stubs.c
+++ b/uisimulator/common/stubs.c
@@ -220,11 +220,6 @@
     (void)pitch;
 }
 
-void audio_set_buffer_margin(int seconds)
-{
-    (void)seconds;
-}
-
 static int sleeptime;
 void set_sleep_timer(int seconds)
 {