From 0570497e3a17563e193e172961ef963e6068df4e Mon Sep 17 00:00:00 2001 From: Björn Stenberg Date: Wed, 14 Aug 2002 19:23:34 +0000 Subject: Added fast forward and rewind (without sound). Patch by Hardeep Sidhu. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@1741 a1c6a512-1295-4272-9138-f99709370657 --- apps/settings.c | 1 + apps/settings.h | 4 ++ apps/settings_menu.c | 7 ++ apps/wps.c | 184 +++++++++++++++++++++++++++++++++++++-------------- firmware/id3.c | 86 +++++++++++++----------- firmware/id3.h | 12 +++- firmware/mpeg.c | 132 ++++++++++++++++++++++++++++++++++++ firmware/mpeg.h | 1 + 8 files changed, 339 insertions(+), 88 deletions(-) diff --git a/apps/settings.c b/apps/settings.c index 6abdd822b5..ef51727105 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -357,6 +357,7 @@ void settings_reset(void) { global_settings.discharge = 0; global_settings.total_uptime = 0; global_settings.scroll_speed = 8; + global_settings.ff_rewind = DEFAULT_FF_REWIND_SETTING; } diff --git a/apps/settings.h b/apps/settings.h index 6175ef942d..115aab0f74 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -69,6 +69,9 @@ struct user_settings /* geeky persistent statistics */ unsigned int total_uptime; /* total uptime since rockbox was first booted */ + + /* FF/Rewind step size (in seconds) */ + int ff_rewind; }; /* prototypes */ @@ -112,5 +115,6 @@ extern struct user_settings global_settings; #define DEFAULT_POWEROFF_SETTING 0 #define DEFAULT_BACKLIGHT_SETTING 5 #define DEFAULT_WPS_DISPLAY 0 +#define DEFAULT_FF_REWIND_SETTING 2 #endif /* __SETTINGS_H__ */ diff --git a/apps/settings_menu.c b/apps/settings_menu.c index 0dbe50d9cb..3def0574a1 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -132,6 +132,12 @@ static void timedate_set(void) } #endif +static void ff_rewind(void) +{ + set_int("[FF/Rewind Step Size]", "s", &global_settings.ff_rewind, + NULL, 1, 1, 999 ); +} + void settings_menu(void) { int m; @@ -152,6 +158,7 @@ void settings_menu(void) #ifdef HAVE_RTC { "Time/Date", timedate_set }, #endif + { "FF/Rewind", ff_rewind }, }; bool old_shuffle = global_settings.playlist_shuffle; diff --git a/apps/wps.c b/apps/wps.c index acb6d90a13..f80cec95a4 100644 --- a/apps/wps.c +++ b/apps/wps.c @@ -54,12 +54,13 @@ #define PLAY_DISPLAY_TRACK_TITLE 2 #ifdef HAVE_RECORDER_KEYPAD -#define RELEASE_MASK (BUTTON_F1 | BUTTON_DOWN) +#define RELEASE_MASK (BUTTON_F1 | BUTTON_DOWN | BUTTON_LEFT | BUTTON_RIGHT) #else -#define RELEASE_MASK (BUTTON_MENU | BUTTON_STOP) +#define RELEASE_MASK (BUTTON_MENU | BUTTON_STOP | BUTTON_LEFT | BUTTON_RIGHT) #endif bool keys_locked = false; +static bool ff_rewind = false; static void draw_screen(struct mp3entry* id3) { @@ -291,6 +292,39 @@ int player_id3_show(void) return(0); } +static void display_file_time(unsigned int elapsed, unsigned int length) +{ + char buffer[32]; + +#ifdef HAVE_LCD_BITMAP + snprintf(buffer,sizeof(buffer), + "Time:%3d:%02d/%d:%02d", + elapsed / 60000, + elapsed % 60000 / 1000, + length / 60000, + length % 60000 / 1000 ); + + lcd_puts(0, 6, buffer); + slidebar(0, LCD_HEIGHT-6, LCD_WIDTH, 6, elapsed*100/length, Grow_Right); + lcd_update(); +#else + /* Display time with the filename scroll only because + the screen has room. */ + if ((global_settings.wps_display == PLAY_DISPLAY_FILENAME_SCROLL) || + ff_rewind ) + { + snprintf(buffer,sizeof(buffer), "%d:%02d/%d:%02d ", + elapsed / 60000, + elapsed % 60000 / 1000, + length / 60000, + length % 60000 / 1000 ); + + lcd_puts(0, 1, buffer); + lcd_update(); + } +#endif +} + void display_keylock_text(bool locked) { lcd_stop_scroll(); @@ -354,7 +388,7 @@ int wps_show(void) bool pending_keylock = true; /* Keylock will go ON next time */ int old_release_mask; int button; - char buffer[32]; + int ff_rewind_count = 0; old_release_mask = button_set_release(RELEASE_MASK); @@ -461,26 +495,112 @@ int wps_show(void) status_draw(); break; - case BUTTON_LEFT: - if (keys_locked) + case BUTTON_LEFT | BUTTON_REPEAT: + if (!keys_locked) { - display_keylock_text(keys_locked); + if (ff_rewind) + { + ff_rewind_count -= global_settings.ff_rewind*1000; + } + else + { + if ( mpeg_is_playing() && id3 && id3->length ) + { + mpeg_pause(); + status_set_playmode(STATUS_FASTBACKWARD); + status_draw(); + ff_rewind = true; + ff_rewind_count = -global_settings.ff_rewind*1000; + } + else + break; + } + + if ((int)(id3->elapsed + ff_rewind_count) < 0) + ff_rewind_count = -id3->elapsed; + + display_file_time(id3->elapsed + ff_rewind_count, + id3->length); + } + break; + + case BUTTON_RIGHT | BUTTON_REPEAT: + if (!keys_locked) + { + if (ff_rewind) + { + ff_rewind_count += global_settings.ff_rewind*1000; + } + else + { + if ( mpeg_is_playing() && id3 && id3->length ) + { + mpeg_pause(); + status_set_playmode(STATUS_FASTFORWARD); + status_draw(); + ff_rewind = true; + ff_rewind_count = global_settings.ff_rewind*1000; + } + else + break; + } + + if ((id3->elapsed + ff_rewind_count) > id3->length) + ff_rewind_count = id3->length - id3->elapsed; + + display_file_time(id3->elapsed + ff_rewind_count, + id3->length); + } + break; + + case BUTTON_LEFT | BUTTON_REL: + if (ff_rewind) + { + /* rewind */ + mpeg_ff_rewind(ff_rewind_count); + ff_rewind_count = 0; + ff_rewind = false; + status_set_playmode(STATUS_PLAY); +#ifdef HAVE_LCD_CHARCELLS draw_screen(id3); - break; +#endif + } + else + { + if (keys_locked) + { + display_keylock_text(keys_locked); + draw_screen(id3); + break; + } + mpeg_prev(); + status_set_playmode(STATUS_PLAY); } - mpeg_prev(); - status_set_playmode(STATUS_PLAY); break; - case BUTTON_RIGHT: - if (keys_locked) + case BUTTON_RIGHT | BUTTON_REL: + if (ff_rewind) { - display_keylock_text(keys_locked); + /* fast forward */ + mpeg_ff_rewind(ff_rewind_count); + ff_rewind_count = 0; + ff_rewind = false; + status_set_playmode(STATUS_PLAY); +#ifdef HAVE_LCD_CHARCELLS draw_screen(id3); - break; +#endif + } + else + { + if (keys_locked) + { + display_keylock_text(keys_locked); + draw_screen(id3); + break; + } + mpeg_next(); + status_set_playmode(STATUS_PLAY); } - mpeg_next(); - status_set_playmode(STATUS_PLAY); break; #ifdef HAVE_PLAYER_KEYPAD @@ -683,39 +803,7 @@ int wps_show(void) #endif case BUTTON_NONE: /* Timeout */ if (mpeg_is_playing() && id3) - { -#ifdef HAVE_LCD_BITMAP - snprintf(buffer,sizeof(buffer), - "Time:%3d:%02d/%d:%02d", - id3->elapsed / 60000, - id3->elapsed % 60000 / 1000, - id3->length / 60000, - id3->length % 60000 / 1000 ); - - lcd_puts(0, 6, buffer); - - slidebar(0, LCD_HEIGHT-6, LCD_WIDTH, 6, - id3->elapsed*100/id3->length, - Grow_Right); - - lcd_update(); -#else - /* Display time with the filename scroll only because - the screen has room. */ - if (global_settings.wps_display == - PLAY_DISPLAY_FILENAME_SCROLL) - { - snprintf(buffer,sizeof(buffer), "%d:%02d/%d:%02d ", - id3->elapsed / 60000, - id3->elapsed % 60000 / 1000, - id3->length / 60000, - id3->length % 60000 / 1000 ); - - lcd_puts(0, 1, buffer); - lcd_update(); - } -#endif - } + display_file_time(id3->elapsed, id3->length); status_draw(); break; diff --git a/firmware/id3.c b/firmware/id3.c index 743ff74902..62150e1ac8 100644 --- a/firmware/id3.c +++ b/firmware/id3.c @@ -383,7 +383,7 @@ static int getsonglength(int fd, struct mp3entry *entry) unsigned int filetime = 0; unsigned long header=0; unsigned char tmp; - unsigned char frame[64]; + unsigned char frame[156]; unsigned char* xing; int version; @@ -412,7 +412,7 @@ static int getsonglength(int fd, struct mp3entry *entry) /* Loop trough file until we find a frame header */ bytecount = 0; - restart: + restart: do { header <<= 8; if(!read(fd, &tmp, 1)) @@ -441,29 +441,29 @@ static int getsonglength(int fd, struct mp3entry *entry) #endif /* MPEG Audio Version */ switch((header & 0x180000) >> 19) { - case 2: - version = 2; - break; - case 3: - version = 1; - break; - default: - goto restart; + case 2: + version = 2; + break; + case 3: + version = 1; + break; + default: + goto restart; } /* Layer */ switch((header & 0x060000) >> 17) { - case 1: - layer = 3; - break; - case 2: - layer = 2; - break; - case 3: - layer = 1; - break; - default: - goto restart; + case 1: + layer = 3; + break; + case 2: + layer = 2; + break; + case 3: + layer = 1; + break; + default: + goto restart; } /* Bitrate */ @@ -488,24 +488,27 @@ static int getsonglength(int fd, struct mp3entry *entry) /* Calculate bytes per frame, calculation depends on layer */ switch(layer) { - case 1: - bpf = bitrate_table[version - 1][layer - 1][bitindex]; - bpf *= 48000; - bpf /= freqtab[version-1][freqindex] << (version - 1); - break; - case 2: - case 3: - bpf = bitrate_table[version - 1][layer - 1][bitindex]; - bpf *= 144000; - bpf /= freqtab[version-1][freqindex] << (version - 1); - break; - default: - bpf = 1; + case 1: + bpf = bitrate_table[version - 1][layer - 1][bitindex]; + bpf *= 48000; + bpf /= freqtab[version-1][freqindex] << (version - 1); + break; + case 2: + case 3: + bpf = bitrate_table[version - 1][layer - 1][bitindex]; + bpf *= 144000; + bpf /= freqtab[version-1][freqindex] << (version - 1); + break; + default: + bpf = 1; } /* Calculate time per frame */ tpf = bs[layer] / (freqtab[version-1][freqindex] << (version - 1)); + entry->bpf = bpf; + entry->tpf = tpf; + /* OK, we have found a frame. Let's see if it has a Xing header */ if(read(fd, frame, sizeof frame) < 0) return -1; @@ -535,21 +538,26 @@ static int getsonglength(int fd, struct mp3entry *entry) /* Yes, it is a VBR file */ entry->vbr = true; - if (xing[7] & 0x01) /* Is the frame count there? */ + if (entry->vbrflags & VBR_FRAMES_FLAG) /* Is the frame count there? */ { int framecount = (xing[8] << 24) | (xing[9] << 16) | (xing[10] << 8) | xing[11]; - + filetime = framecount * tpf; } - if (xing[7] & 0x02) /* is byte count there? */ + + if (entry->vbrflags & VBR_BYTES_FLAG) /* is byte count there? */ { int bytecount = (xing[12] << 24) | (xing[13] << 16) | (xing[14] << 8) | xing[15]; - + bitrate = bytecount * 8 / filetime; } - /* We don't care about the TOC just yet. Maybe another time. */ + + if (entry->vbrflags & VBR_TOC_FLAG) /* is table-of-contents there? */ + { + memcpy( entry->toc, xing+16, 100 ); + } } entry->bitrate = bitrate; diff --git a/firmware/id3.h b/firmware/id3.h index 203e997073..1a5bc7441e 100644 --- a/firmware/id3.h +++ b/firmware/id3.h @@ -29,7 +29,6 @@ struct mp3entry { int tracknum; int version; int layer; - bool vbr; unsigned int bitrate; unsigned int frequency; unsigned int id3v2len; @@ -37,12 +36,23 @@ struct mp3entry { unsigned int filesize; /* in bytes */ unsigned int length; /* song length */ unsigned int elapsed; /* ms played */ + long bpf; /* bytes per frame */ + long tpf; /* time per frame */ + + /* Xing VBR fields */ + bool vbr; + unsigned char vbrflags; + unsigned char toc[100];/* table of contents */ /* these following two fields are used for local buffering */ char id3v2buf[300]; char id3v1buf[3][32]; }; +#define VBR_FRAMES_FLAG 0x01 +#define VBR_BYTES_FLAG 0x02 +#define VBR_TOC_FLAG 0x04 + bool mp3info(struct mp3entry *entry, char *filename); #endif diff --git a/firmware/mpeg.c b/firmware/mpeg.c index ab2fefc872..4c75345f66 100644 --- a/firmware/mpeg.c +++ b/firmware/mpeg.c @@ -47,6 +47,7 @@ #define MPEG_RESUME 4 #define MPEG_NEXT 5 #define MPEG_PREV 6 +#define MPEG_FF_REWIND 7 #define MPEG_NEED_DATA 100 #define MPEG_SWAP_DATA 101 #define MPEG_TRACK_CHANGE 102 @@ -216,6 +217,22 @@ static void remove_current_tag(void) } } +static void remove_all_non_current_tags(void) +{ + int i = (tag_read_idx+1) & MAX_ID3_TAGS_MASK; + + while (i != tag_write_idx) + { + id3tags[i]->used = false; + id3tags[i] = NULL; + + i = (i+1) & MAX_ID3_TAGS_MASK; + } + + tag_write_idx = (tag_read_idx+1) & MAX_ID3_TAGS_MASK; + debug_tags(); +} + static void remove_all_tags(void) { int i; @@ -823,6 +840,112 @@ static void mpeg_thread(void) break; } + case MPEG_FF_REWIND: { + struct mp3entry *id3 = mpeg_current_track(); + int newtime = id3->elapsed + (int)ev.data; + int curpos, newpos, diffpos; + DEBUGF("MPEG_FF_REWIND\n"); + + if (id3->vbr && (id3->vbrflags & VBR_TOC_FLAG)) + { + /* Use the TOC to find the new position */ + int percent = (newtime*100)/id3->length; + int curtoc, nexttoc, nextpos, remainder; + + if (percent > 99) + percent = 99; + + curtoc = id3->toc[percent]; + + if (percent < 99) + nexttoc = id3->toc[percent+1]; + else + nexttoc = 256; + + newpos = (curtoc*id3->filesize)/256; + + /* Use the remainder to get a more accurate position */ + nextpos = (nexttoc*id3->filesize)/256; + remainder = (newtime*10000)/id3->length - (percent*100); + newpos += ((nextpos-newpos)*remainder)/100; + } + else if (id3->bpf && id3->tpf) + newpos = (newtime*id3->bpf)/id3->tpf; + else + /* Not enough information to FF/Rewind */ + break; + + newpos = newpos & ~1; + curpos = lseek(mpeg_file, 0, SEEK_CUR); + + if (num_tracks_in_memory() > 1) + { + /* We have started loading other tracks that need to be + accounted for */ + int i = tag_read_idx; + int j = tag_write_idx - 1; + + if (j < 0) + j = MAX_ID3_TAGS - 1; + + while (i != j) + { + curpos += id3tags[i]->id3.filesize; + i = (i+1) & MAX_ID3_TAGS_MASK; + } + } + + diffpos = curpos - newpos; + +#warning "Borde inte mp3buflen vara mp3buf_write?" + if(diffpos >= 0 && diffpos < mp3buflen) + { + /* We are changing to a position that's already in + memory */ + mp3buf_read = mp3buf_write - diffpos; + if (mp3buf_read < 0) + { + mp3buf_read += mp3buflen; + } + + playing = true; + last_dma_tick = current_tick; + init_dma(); + start_dma(); + } + else + { + /* Move to the new position in the file and start + loading data */ + reset_mp3_buffer(); + + if (num_tracks_in_memory() > 1) + { + /* We have to reload the current track */ + close(mpeg_file); + remove_all_non_current_tags(); + + mpeg_file = open(id3->path, O_RDONLY); + if (mpeg_file < 0) + break; + } + + if(-1 == lseek(mpeg_file, newpos, SEEK_SET)) + break; + + filling = true; + queue_post(&mpeg_queue, MPEG_NEED_DATA, 0); + + /* Tell the file loading code that we want to start playing + as soon as we have some data */ + play_pending = true; + } + + id3->elapsed = newtime; + + break; + } + case MPEG_SWAP_DATA: free_space_left = mp3buf_write - mp3buf_swapwrite; @@ -1128,6 +1251,15 @@ void mpeg_prev(void) #endif } +void mpeg_ff_rewind(int change) +{ +#ifndef SIMULATOR + queue_post(&mpeg_queue, MPEG_FF_REWIND, (void *)change); +#else + (void)change; +#endif +} + bool mpeg_is_playing(void) { return playing || play_pending; diff --git a/firmware/mpeg.h b/firmware/mpeg.h index 4ea0001e25..2838c47d45 100644 --- a/firmware/mpeg.h +++ b/firmware/mpeg.h @@ -28,6 +28,7 @@ void mpeg_pause(void); void mpeg_resume(void); void mpeg_next(void); void mpeg_prev(void); +void mpeg_ff_rewind(int change); bool mpeg_is_playing(void); void mpeg_sound_set(int setting, int value); int mpeg_sound_min(int setting); -- cgit v1.2.3