summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Stenberg <bjorn@haxx.se>2002-08-14 19:23:34 +0000
committerBjörn Stenberg <bjorn@haxx.se>2002-08-14 19:23:34 +0000
commit0570497e3a17563e193e172961ef963e6068df4e (patch)
tree2bfd3300b2e03fdf81c2dca2ee56b4666caafd80
parentb42ac1dc4dc389de56fa299dc2c2bfcf2686c435 (diff)
downloadrockbox-0570497e3a17563e193e172961ef963e6068df4e.zip
rockbox-0570497e3a17563e193e172961ef963e6068df4e.tar.gz
rockbox-0570497e3a17563e193e172961ef963e6068df4e.tar.bz2
rockbox-0570497e3a17563e193e172961ef963e6068df4e.tar.xz
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
-rw-r--r--apps/settings.c1
-rw-r--r--apps/settings.h4
-rw-r--r--apps/settings_menu.c7
-rw-r--r--apps/wps.c184
-rw-r--r--firmware/id3.c86
-rw-r--r--firmware/id3.h12
-rw-r--r--firmware/mpeg.c132
-rw-r--r--firmware/mpeg.h1
8 files changed, 339 insertions, 88 deletions
diff --git a/apps/settings.c b/apps/settings.c
index 6abdd82..ef51727 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 6175ef9..115aab0 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 0dbe50d..3def057 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 acb6d90..f80cec9 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 743ff74..62150e1 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 203e997..1a5bc74 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 ab2fefc..4c75345 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 4ea0001..2838c47 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);