diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 4d3c90c8e3..21e30e9a08 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -230,6 +230,7 @@ bool dbg_audio_thread(void) int button; int line; bool done = false; + int bufsize = pcmbuf_get_bufsize(); ticks = boost_ticks = 0; @@ -253,12 +254,12 @@ bool dbg_audio_thread(void) 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 182d464649..90be703c6b 100644 --- a/apps/main.c +++ b/apps/main.c @@ -106,6 +106,7 @@ void init(void) font_init(); show_logo(); lang_init(); + audio_init(); settings_reset(); settings_calc_config_sector(); settings_load(SETTINGS_ALL); @@ -127,8 +128,6 @@ void init(void) 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 @@ void init(void) } } + /* 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 @@ void init(void) 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 bcf971d1f8..d3f4d15a48 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c @@ -32,15 +32,17 @@ #include "system.h" #include #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 @@ volatile int pcmbuf_write_index; 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 @@ static void pcmbuf_callback(unsigned char** start, long* size) { 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 @@ static void pcmbuf_callback(unsigned char** start, long* size) if(pcmbuf_num_used_buffers()) { - pcmbuf_unplayed_bytes -= desc->size; - audiobuffer_free += desc->size; *start = desc->addr; *size = desc->size; @@ -142,6 +145,7 @@ static void pcmbuf_callback(unsigned char** start, long* size) pcmbuf_event_handler(); } + last_chunksize = *size; if(pcmbuf_unplayed_bytes <= pcmbuf_watermark) { if(pcmbuf_watermark_event) @@ -199,7 +203,7 @@ unsigned int pcmbuf_get_latency(void) 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 @@ bool pcmbuf_is_lowdata(void) 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_is_lowdata(void) 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 @@ bool pcmbuf_crossfade_init(void) 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_play_stop(void) } -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 @@ void pcmbuf_flush_fillpos(void) } 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 @@ static void crossfade_start(void) crossfade_pos -= crossfade_amount*2; if (crossfade_pos < 0) - crossfade_pos += PCMBUF_SIZE; + crossfade_pos += pcmbuf_size; } static __inline @@ -383,7 +393,7 @@ static bool prepare_insert(long length) 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 @@ void* pcmbuf_request_buffer(long length, long *realsize) *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 @@ void pcmbuf_flush_buffer(long length) 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 @@ void pcmbuf_flush_buffer(long length) 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 @@ bool pcmbuf_insert_buffer(char *buf, long length) 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 @@ bool pcmbuf_insert_buffer(char *buf, long length) } 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 @@ bool pcmbuf_insert_buffer(char *buf, long length) 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 2b4023fb98..29217afc9b 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 4929cf3477..bf8e5bdbaf 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -172,6 +172,7 @@ void (*track_unbuffer_callback)(struct mp3entry *id3, bool last_track); static int conf_bufferlimit; static int conf_watermark; static int conf_filechunk; +static int buffer_margin; static bool v1first = false; @@ -468,11 +469,21 @@ bool codec_seek_buffer_callback(off_t newpos) 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 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) 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 @@ int mp3_get_file_pos(void) 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); +} + +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); } -#endif void mpeg_id3_options(bool _v1first) { @@ -1786,9 +1821,6 @@ void test_unbuffer_event(struct mp3entry *id3, bool last_track) 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 @@ void audio_init(void) /* 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 @@ void audio_init(void) 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 524dfcd190..e9f45de130 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -77,7 +77,7 @@ const char rec_base_directory[] = REC_BASE_DIR; #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 @@ static const struct bit_entry rtc_bits[] = "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 @@ static const struct bit_entry hd_bits[] = {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 @@ static const struct bit_entry hd_bits[] = #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 @@ void settings_apply(void) } #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 a94dda8b51..490970d1f6 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -917,12 +917,33 @@ static bool max_files_in_playlist(void) 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 @@ static bool id3_order(void) #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 731db172c0..dfe08e5d3a 100644 --- a/firmware/mp3_playback.c +++ b/firmware/mp3_playback.c @@ -621,7 +621,7 @@ void mp3_init(int volume, int bass, int treble, int balance, int loudness, 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 9643937684..446d40c661 100644 --- a/uisimulator/common/stubs.c +++ b/uisimulator/common/stubs.c @@ -220,11 +220,6 @@ void mpeg_set_pitch(int pitch) (void)pitch; } -void audio_set_buffer_margin(int seconds) -{ - (void)seconds; -} - static int sleeptime; void set_sleep_timer(int seconds) {