Rockbox.org home
release
dev builds
extras
themes manual
wiki
device status forums
mailing lists
IRC bugs
patches
dev guide



Rockbox mail archive

Subject: [PATCH] Fast-Forward/Rewind

[PATCH] Fast-Forward/Rewind

From: Hardeep Sidhu <hardeeps_at_pobox.com>
Date: Wed, 14 Aug 2002 10:43:55 -0700

The patch below implements fast-forward and rewind. The interface is the same
as the stock firmware (hold right for fast-forward and left for rewind) but the
behaviour is somewhat different: as soon as you start the fast-forward/rewind
the mp3 is paused and it doesn't resume until you release the button. The step
size (in seconds) can be configured from the "General Settings" menu. The
ff/rewind progress is displayed using the elapsed time display which means the
Player will only see it when in filename scroll mode. Anyone have a better idea
on how to display progress on the Player?

I have only tested the patch on my AJBR6 and am anxious to hear how well it
works on other players/recorders.

TODO:
- Store the step size setting
- Simulator implementation

-Hardeep

P.S. Congratulations on the 1.2 release, it's awesome!

Index: apps/settings.c
===================================================================
RCS file: /cvsroot/rockbox/apps/settings.c,v
retrieving revision 1.27
diff -u -b -r1.27 settings.c
--- apps/settings.c 14 Aug 2002 12:30:31 -0000 1.27
+++ apps/settings.c 14 Aug 2002 17:12:23 -0000
_at__at_ -357,6 +357,7 _at__at_
     global_settings.discharge = 0;
     global_settings.total_uptime = 0;
     global_settings.scroll_speed = 8;
+ global_settings.ff_rewind = DEFAULT_FF_REWIND_SETTING;
 }


Index: apps/settings.h
===================================================================
RCS file: /cvsroot/rockbox/apps/settings.h,v
retrieving revision 1.17
diff -u -b -r1.17 settings.h
--- apps/settings.h 13 Aug 2002 17:16:08 -0000 1.17
+++ apps/settings.h 14 Aug 2002 17:12:23 -0000
_at__at_ -69,6 +69,9 _at__at_

     /* geeky persistent statistics */
     unsigned int total_uptime; /* total uptime since rockbox was first booted
*/
+
+ /* FF/Rewind step size (in seconds) */
+ int ff_rewind;
 };

 /* prototypes */
_at__at_ -112,5 +115,6 _at__at_
 #define DEFAULT_POWEROFF_SETTING 0
 #define DEFAULT_BACKLIGHT_SETTING 5
 #define DEFAULT_WPS_DISPLAY 0
+#define DEFAULT_FF_REWIND_SETTING 2

 #endif /* __SETTINGS_H__ */
Index: apps/settings_menu.c
===================================================================
RCS file: /cvsroot/rockbox/apps/settings_menu.c,v
retrieving revision 1.14
diff -u -b -r1.14 settings_menu.c
--- apps/settings_menu.c 13 Aug 2002 11:10:11 -0000 1.14
+++ apps/settings_menu.c 14 Aug 2002 17:12:24 -0000
_at__at_ -132,6 +132,12 _at__at_
 }
 #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;
_at__at_ -152,6 +158,7 _at__at_
 #ifdef HAVE_RTC
         { "Time/Date", timedate_set },
 #endif
+ { "FF/Rewind", ff_rewind },
     };
     bool old_shuffle = global_settings.playlist_shuffle;

Index: apps/wps.c
===================================================================
RCS file: /cvsroot/rockbox/apps/wps.c,v
retrieving revision 1.63
diff -u -b -r1.63 wps.c
--- apps/wps.c 13 Aug 2002 20:55:08 -0000 1.63
+++ apps/wps.c 14 Aug 2002 17:12:26 -0000
_at__at_ -54,9 +54,9 _at__at_
 #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;
_at__at_ -291,6 +291,41 _at__at_
     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)
+ {
+ 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();
_at__at_ -352,9 +387,10 _at__at_
     bool dont_go_to_menu = false;
     bool menu_button_is_down = false;
     bool pending_keylock = true; /* Keylock will go ON next time */
+ bool ff_rewind = false;
     int old_release_mask;
     int button;
- char buffer[32];
+ int ff_rewind_count = 0;

     old_release_mask = button_set_release(RELEASE_MASK);

_at__at_ -461,7 +497,75 _at__at_
                 status_draw();
                 break;

- case BUTTON_LEFT:
+ case BUTTON_LEFT | BUTTON_REPEAT:
+ if (!keys_locked)
+ {
+ if (!ff_rewind)
+ {
+ 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;
+ }
+ }
+ else
+ {
+ ff_rewind_count -= global_settings.ff_rewind*1000;
+ }
+
+ 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)
+ {
+ 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;
+ }
+ }
+ else
+ {
+ ff_rewind_count += global_settings.ff_rewind*1000;
+ }
+
+ 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)
+ {
                 if (keys_locked)
                 {
                     display_keylock_text(keys_locked);
_at__at_ -470,9 +574,20 _at__at_
                 }
                 mpeg_prev();
                 status_set_playmode(STATUS_PLAY);
+ }
+ else
+ {
+ /* rewind */
+ mpeg_ff_rewind(ff_rewind_count);
+ ff_rewind_count = 0;
+ ff_rewind = false;
+ status_set_playmode(STATUS_PLAY);
+ }
                 break;

- case BUTTON_RIGHT:
+ case BUTTON_RIGHT | BUTTON_REL:
+ if (!ff_rewind)
+ {
                 if (keys_locked)
                 {
                     display_keylock_text(keys_locked);
_at__at_ -481,6 +596,15 _at__at_
                 }
                 mpeg_next();
                 status_set_playmode(STATUS_PLAY);
+ }
+ else
+ {
+ /* fast forward */
+ mpeg_ff_rewind(ff_rewind_count);
+ ff_rewind_count = 0;
+ ff_rewind = false;
+ status_set_playmode(STATUS_PLAY);
+ }
                 break;

 #ifdef HAVE_PLAYER_KEYPAD
_at__at_ -684,37 +808,7 _at__at_
             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();
Index: firmware/id3.c
===================================================================
RCS file: /cvsroot/rockbox/firmware/id3.c,v
retrieving revision 1.35
diff -u -b -r1.35 id3.c
--- firmware/id3.c 14 Aug 2002 10:15:27 -0000 1.35
+++ firmware/id3.c 14 Aug 2002 17:12:27 -0000
_at__at_ -383,7 +383,7 _at__at_
     unsigned int filetime = 0;
     unsigned long header=0;
     unsigned char tmp;
- unsigned char frame[64];
+ unsigned char frame[156];
     unsigned char* xing;

     int version;
_at__at_ -506,6 +506,9 _at__at_
     /* 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;
_at__at_ -534,22 +537,31 _at__at_
     {
         /* Yes, it is a VBR file */
         entry->vbr = true;
+ entry->vbrflags = xing[7];

- 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) /* TOC */
+ {
+ int i;
+
+ for(i=0; i<100; i++)
+ {
+ entry->toc[i] = xing[16+i];
+ }
+ }
     }

     entry->bitrate = bitrate;
Index: firmware/id3.h
===================================================================
RCS file: /cvsroot/rockbox/firmware/id3.h,v
retrieving revision 1.12
diff -u -b -r1.12 id3.h
--- firmware/id3.h 15 Jul 2002 23:05:44 -0000 1.12
+++ firmware/id3.h 14 Aug 2002 17:12:27 -0000
_at__at_ -29,7 +29,6 _at__at_
     int tracknum;
     int version;
     int layer;
- bool vbr;
     unsigned int bitrate;
     unsigned int frequency;
     unsigned int id3v2len;
_at__at_ -37,11 +36,22 _at__at_
     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);

Index: firmware/mpeg.c
===================================================================
RCS file: /cvsroot/rockbox/firmware/mpeg.c,v
retrieving revision 1.86
diff -u -b -r1.86 mpeg.c
--- firmware/mpeg.c 14 Aug 2002 12:54:37 -0000 1.86
+++ firmware/mpeg.c 14 Aug 2002 17:12:30 -0000
_at__at_ -47,6 +47,7 _at__at_
 #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
_at__at_ -216,6 +217,22 _at__at_
     }
 }

+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;
_at__at_ -823,6 +840,110 _at__at_
                 break;
             }

+ case MPEG_FF_REWIND: {
+ struct mp3entry *id3 = mpeg_current_track();
+ int newtime = id3->elapsed + (int)ev.data;
+ int curpos, newpos, diffpos;
+
+ 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;
+
+ 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;

_at__at_ -1125,6 +1246,14 _at__at_
     mp3info(&taginfo, file);
     current_track_counter--;
     playing = true;
+#endif
+}
+
+void mpeg_ff_rewind(int change)
+{
+#ifndef SIMULATOR
+ queue_post(&mpeg_queue, MPEG_FF_REWIND, (void *)change);
+#else
 #endif
 }

Index: firmware/mpeg.h
===================================================================
RCS file: /cvsroot/rockbox/firmware/mpeg.h,v
retrieving revision 1.13
diff -u -b -r1.13 mpeg.h
--- firmware/mpeg.h 8 Aug 2002 20:44:25 -0000 1.13
+++ firmware/mpeg.h 14 Aug 2002 17:12:30 -0000
_at__at_ -28,6 +28,7 _at__at_
 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);
Received on 2002-08-14

Page template was last modified "Tue Sep 7 00:00:02 2021" The Rockbox Crew -- Privacy Policy