diff options
author | Björn Stenberg <bjorn@haxx.se> | 2002-08-14 19:23:34 +0000 |
---|---|---|
committer | Björn Stenberg <bjorn@haxx.se> | 2002-08-14 19:23:34 +0000 |
commit | 0570497e3a17563e193e172961ef963e6068df4e (patch) | |
tree | 2bfd3300b2e03fdf81c2dca2ee56b4666caafd80 | |
parent | b42ac1dc4dc389de56fa299dc2c2bfcf2686c435 (diff) | |
download | rockbox-0570497e3a17563e193e172961ef963e6068df4e.tar.gz rockbox-0570497e3a17563e193e172961ef963e6068df4e.zip |
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.c | 1 | ||||
-rw-r--r-- | apps/settings.h | 4 | ||||
-rw-r--r-- | apps/settings_menu.c | 7 | ||||
-rw-r--r-- | apps/wps.c | 184 | ||||
-rw-r--r-- | firmware/id3.c | 86 | ||||
-rw-r--r-- | firmware/id3.h | 12 | ||||
-rw-r--r-- | firmware/mpeg.c | 132 | ||||
-rw-r--r-- | 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) { | |||
357 | global_settings.discharge = 0; | 357 | global_settings.discharge = 0; |
358 | global_settings.total_uptime = 0; | 358 | global_settings.total_uptime = 0; |
359 | global_settings.scroll_speed = 8; | 359 | global_settings.scroll_speed = 8; |
360 | global_settings.ff_rewind = DEFAULT_FF_REWIND_SETTING; | ||
360 | } | 361 | } |
361 | 362 | ||
362 | 363 | ||
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 | |||
69 | 69 | ||
70 | /* geeky persistent statistics */ | 70 | /* geeky persistent statistics */ |
71 | unsigned int total_uptime; /* total uptime since rockbox was first booted */ | 71 | unsigned int total_uptime; /* total uptime since rockbox was first booted */ |
72 | |||
73 | /* FF/Rewind step size (in seconds) */ | ||
74 | int ff_rewind; | ||
72 | }; | 75 | }; |
73 | 76 | ||
74 | /* prototypes */ | 77 | /* prototypes */ |
@@ -112,5 +115,6 @@ extern struct user_settings global_settings; | |||
112 | #define DEFAULT_POWEROFF_SETTING 0 | 115 | #define DEFAULT_POWEROFF_SETTING 0 |
113 | #define DEFAULT_BACKLIGHT_SETTING 5 | 116 | #define DEFAULT_BACKLIGHT_SETTING 5 |
114 | #define DEFAULT_WPS_DISPLAY 0 | 117 | #define DEFAULT_WPS_DISPLAY 0 |
118 | #define DEFAULT_FF_REWIND_SETTING 2 | ||
115 | 119 | ||
116 | #endif /* __SETTINGS_H__ */ | 120 | #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) | |||
132 | } | 132 | } |
133 | #endif | 133 | #endif |
134 | 134 | ||
135 | static void ff_rewind(void) | ||
136 | { | ||
137 | set_int("[FF/Rewind Step Size]", "s", &global_settings.ff_rewind, | ||
138 | NULL, 1, 1, 999 ); | ||
139 | } | ||
140 | |||
135 | void settings_menu(void) | 141 | void settings_menu(void) |
136 | { | 142 | { |
137 | int m; | 143 | int m; |
@@ -152,6 +158,7 @@ void settings_menu(void) | |||
152 | #ifdef HAVE_RTC | 158 | #ifdef HAVE_RTC |
153 | { "Time/Date", timedate_set }, | 159 | { "Time/Date", timedate_set }, |
154 | #endif | 160 | #endif |
161 | { "FF/Rewind", ff_rewind }, | ||
155 | }; | 162 | }; |
156 | bool old_shuffle = global_settings.playlist_shuffle; | 163 | bool old_shuffle = global_settings.playlist_shuffle; |
157 | 164 | ||
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 @@ | |||
54 | #define PLAY_DISPLAY_TRACK_TITLE 2 | 54 | #define PLAY_DISPLAY_TRACK_TITLE 2 |
55 | 55 | ||
56 | #ifdef HAVE_RECORDER_KEYPAD | 56 | #ifdef HAVE_RECORDER_KEYPAD |
57 | #define RELEASE_MASK (BUTTON_F1 | BUTTON_DOWN) | 57 | #define RELEASE_MASK (BUTTON_F1 | BUTTON_DOWN | BUTTON_LEFT | BUTTON_RIGHT) |
58 | #else | 58 | #else |
59 | #define RELEASE_MASK (BUTTON_MENU | BUTTON_STOP) | 59 | #define RELEASE_MASK (BUTTON_MENU | BUTTON_STOP | BUTTON_LEFT | BUTTON_RIGHT) |
60 | #endif | 60 | #endif |
61 | 61 | ||
62 | bool keys_locked = false; | 62 | bool keys_locked = false; |
63 | static bool ff_rewind = false; | ||
63 | 64 | ||
64 | static void draw_screen(struct mp3entry* id3) | 65 | static void draw_screen(struct mp3entry* id3) |
65 | { | 66 | { |
@@ -291,6 +292,39 @@ int player_id3_show(void) | |||
291 | return(0); | 292 | return(0); |
292 | } | 293 | } |
293 | 294 | ||
295 | static void display_file_time(unsigned int elapsed, unsigned int length) | ||
296 | { | ||
297 | char buffer[32]; | ||
298 | |||
299 | #ifdef HAVE_LCD_BITMAP | ||
300 | snprintf(buffer,sizeof(buffer), | ||
301 | "Time:%3d:%02d/%d:%02d", | ||
302 | elapsed / 60000, | ||
303 | elapsed % 60000 / 1000, | ||
304 | length / 60000, | ||
305 | length % 60000 / 1000 ); | ||
306 | |||
307 | lcd_puts(0, 6, buffer); | ||
308 | slidebar(0, LCD_HEIGHT-6, LCD_WIDTH, 6, elapsed*100/length, Grow_Right); | ||
309 | lcd_update(); | ||
310 | #else | ||
311 | /* Display time with the filename scroll only because | ||
312 | the screen has room. */ | ||
313 | if ((global_settings.wps_display == PLAY_DISPLAY_FILENAME_SCROLL) || | ||
314 | ff_rewind ) | ||
315 | { | ||
316 | snprintf(buffer,sizeof(buffer), "%d:%02d/%d:%02d ", | ||
317 | elapsed / 60000, | ||
318 | elapsed % 60000 / 1000, | ||
319 | length / 60000, | ||
320 | length % 60000 / 1000 ); | ||
321 | |||
322 | lcd_puts(0, 1, buffer); | ||
323 | lcd_update(); | ||
324 | } | ||
325 | #endif | ||
326 | } | ||
327 | |||
294 | void display_keylock_text(bool locked) | 328 | void display_keylock_text(bool locked) |
295 | { | 329 | { |
296 | lcd_stop_scroll(); | 330 | lcd_stop_scroll(); |
@@ -354,7 +388,7 @@ int wps_show(void) | |||
354 | bool pending_keylock = true; /* Keylock will go ON next time */ | 388 | bool pending_keylock = true; /* Keylock will go ON next time */ |
355 | int old_release_mask; | 389 | int old_release_mask; |
356 | int button; | 390 | int button; |
357 | char buffer[32]; | 391 | int ff_rewind_count = 0; |
358 | 392 | ||
359 | old_release_mask = button_set_release(RELEASE_MASK); | 393 | old_release_mask = button_set_release(RELEASE_MASK); |
360 | 394 | ||
@@ -461,26 +495,112 @@ int wps_show(void) | |||
461 | status_draw(); | 495 | status_draw(); |
462 | break; | 496 | break; |
463 | 497 | ||
464 | case BUTTON_LEFT: | 498 | case BUTTON_LEFT | BUTTON_REPEAT: |
465 | if (keys_locked) | 499 | if (!keys_locked) |
466 | { | 500 | { |
467 | display_keylock_text(keys_locked); | 501 | if (ff_rewind) |
502 | { | ||
503 | ff_rewind_count -= global_settings.ff_rewind*1000; | ||
504 | } | ||
505 | else | ||
506 | { | ||
507 | if ( mpeg_is_playing() && id3 && id3->length ) | ||
508 | { | ||
509 | mpeg_pause(); | ||
510 | status_set_playmode(STATUS_FASTBACKWARD); | ||
511 | status_draw(); | ||
512 | ff_rewind = true; | ||
513 | ff_rewind_count = -global_settings.ff_rewind*1000; | ||
514 | } | ||
515 | else | ||
516 | break; | ||
517 | } | ||
518 | |||
519 | if ((int)(id3->elapsed + ff_rewind_count) < 0) | ||
520 | ff_rewind_count = -id3->elapsed; | ||
521 | |||
522 | display_file_time(id3->elapsed + ff_rewind_count, | ||
523 | id3->length); | ||
524 | } | ||
525 | break; | ||
526 | |||
527 | case BUTTON_RIGHT | BUTTON_REPEAT: | ||
528 | if (!keys_locked) | ||
529 | { | ||
530 | if (ff_rewind) | ||
531 | { | ||
532 | ff_rewind_count += global_settings.ff_rewind*1000; | ||
533 | } | ||
534 | else | ||
535 | { | ||
536 | if ( mpeg_is_playing() && id3 && id3->length ) | ||
537 | { | ||
538 | mpeg_pause(); | ||
539 | status_set_playmode(STATUS_FASTFORWARD); | ||
540 | status_draw(); | ||
541 | ff_rewind = true; | ||
542 | ff_rewind_count = global_settings.ff_rewind*1000; | ||
543 | } | ||
544 | else | ||
545 | break; | ||
546 | } | ||
547 | |||
548 | if ((id3->elapsed + ff_rewind_count) > id3->length) | ||
549 | ff_rewind_count = id3->length - id3->elapsed; | ||
550 | |||
551 | display_file_time(id3->elapsed + ff_rewind_count, | ||
552 | id3->length); | ||
553 | } | ||
554 | break; | ||
555 | |||
556 | case BUTTON_LEFT | BUTTON_REL: | ||
557 | if (ff_rewind) | ||
558 | { | ||
559 | /* rewind */ | ||
560 | mpeg_ff_rewind(ff_rewind_count); | ||
561 | ff_rewind_count = 0; | ||
562 | ff_rewind = false; | ||
563 | status_set_playmode(STATUS_PLAY); | ||
564 | #ifdef HAVE_LCD_CHARCELLS | ||
468 | draw_screen(id3); | 565 | draw_screen(id3); |
469 | break; | 566 | #endif |
567 | } | ||
568 | else | ||
569 | { | ||
570 | if (keys_locked) | ||
571 | { | ||
572 | display_keylock_text(keys_locked); | ||
573 | draw_screen(id3); | ||
574 | break; | ||
575 | } | ||
576 | mpeg_prev(); | ||
577 | status_set_playmode(STATUS_PLAY); | ||
470 | } | 578 | } |
471 | mpeg_prev(); | ||
472 | status_set_playmode(STATUS_PLAY); | ||
473 | break; | 579 | break; |
474 | 580 | ||
475 | case BUTTON_RIGHT: | 581 | case BUTTON_RIGHT | BUTTON_REL: |
476 | if (keys_locked) | 582 | if (ff_rewind) |
477 | { | 583 | { |
478 | display_keylock_text(keys_locked); | 584 | /* fast forward */ |
585 | mpeg_ff_rewind(ff_rewind_count); | ||
586 | ff_rewind_count = 0; | ||
587 | ff_rewind = false; | ||
588 | status_set_playmode(STATUS_PLAY); | ||
589 | #ifdef HAVE_LCD_CHARCELLS | ||
479 | draw_screen(id3); | 590 | draw_screen(id3); |
480 | break; | 591 | #endif |
592 | } | ||
593 | else | ||
594 | { | ||
595 | if (keys_locked) | ||
596 | { | ||
597 | display_keylock_text(keys_locked); | ||
598 | draw_screen(id3); | ||
599 | break; | ||
600 | } | ||
601 | mpeg_next(); | ||
602 | status_set_playmode(STATUS_PLAY); | ||
481 | } | 603 | } |
482 | mpeg_next(); | ||
483 | status_set_playmode(STATUS_PLAY); | ||
484 | break; | 604 | break; |
485 | 605 | ||
486 | #ifdef HAVE_PLAYER_KEYPAD | 606 | #ifdef HAVE_PLAYER_KEYPAD |
@@ -683,39 +803,7 @@ int wps_show(void) | |||
683 | #endif | 803 | #endif |
684 | case BUTTON_NONE: /* Timeout */ | 804 | case BUTTON_NONE: /* Timeout */ |
685 | if (mpeg_is_playing() && id3) | 805 | if (mpeg_is_playing() && id3) |
686 | { | 806 | display_file_time(id3->elapsed, id3->length); |
687 | #ifdef HAVE_LCD_BITMAP | ||
688 | snprintf(buffer,sizeof(buffer), | ||
689 | "Time:%3d:%02d/%d:%02d", | ||
690 | id3->elapsed / 60000, | ||
691 | id3->elapsed % 60000 / 1000, | ||
692 | id3->length / 60000, | ||
693 | id3->length % 60000 / 1000 ); | ||
694 | |||
695 | lcd_puts(0, 6, buffer); | ||
696 | |||
697 | slidebar(0, LCD_HEIGHT-6, LCD_WIDTH, 6, | ||
698 | id3->elapsed*100/id3->length, | ||
699 | Grow_Right); | ||
700 | |||
701 | lcd_update(); | ||
702 | #else | ||
703 | /* Display time with the filename scroll only because | ||
704 | the screen has room. */ | ||
705 | if (global_settings.wps_display == | ||
706 | PLAY_DISPLAY_FILENAME_SCROLL) | ||
707 | { | ||
708 | snprintf(buffer,sizeof(buffer), "%d:%02d/%d:%02d ", | ||
709 | id3->elapsed / 60000, | ||
710 | id3->elapsed % 60000 / 1000, | ||
711 | id3->length / 60000, | ||
712 | id3->length % 60000 / 1000 ); | ||
713 | |||
714 | lcd_puts(0, 1, buffer); | ||
715 | lcd_update(); | ||
716 | } | ||
717 | #endif | ||
718 | } | ||
719 | 807 | ||
720 | status_draw(); | 808 | status_draw(); |
721 | break; | 809 | 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) | |||
383 | unsigned int filetime = 0; | 383 | unsigned int filetime = 0; |
384 | unsigned long header=0; | 384 | unsigned long header=0; |
385 | unsigned char tmp; | 385 | unsigned char tmp; |
386 | unsigned char frame[64]; | 386 | unsigned char frame[156]; |
387 | unsigned char* xing; | 387 | unsigned char* xing; |
388 | 388 | ||
389 | int version; | 389 | int version; |
@@ -412,7 +412,7 @@ static int getsonglength(int fd, struct mp3entry *entry) | |||
412 | 412 | ||
413 | /* Loop trough file until we find a frame header */ | 413 | /* Loop trough file until we find a frame header */ |
414 | bytecount = 0; | 414 | bytecount = 0; |
415 | restart: | 415 | restart: |
416 | do { | 416 | do { |
417 | header <<= 8; | 417 | header <<= 8; |
418 | if(!read(fd, &tmp, 1)) | 418 | if(!read(fd, &tmp, 1)) |
@@ -441,29 +441,29 @@ static int getsonglength(int fd, struct mp3entry *entry) | |||
441 | #endif | 441 | #endif |
442 | /* MPEG Audio Version */ | 442 | /* MPEG Audio Version */ |
443 | switch((header & 0x180000) >> 19) { | 443 | switch((header & 0x180000) >> 19) { |
444 | case 2: | 444 | case 2: |
445 | version = 2; | 445 | version = 2; |
446 | break; | 446 | break; |
447 | case 3: | 447 | case 3: |
448 | version = 1; | 448 | version = 1; |
449 | break; | 449 | break; |
450 | default: | 450 | default: |
451 | goto restart; | 451 | goto restart; |
452 | } | 452 | } |
453 | 453 | ||
454 | /* Layer */ | 454 | /* Layer */ |
455 | switch((header & 0x060000) >> 17) { | 455 | switch((header & 0x060000) >> 17) { |
456 | case 1: | 456 | case 1: |
457 | layer = 3; | 457 | layer = 3; |
458 | break; | 458 | break; |
459 | case 2: | 459 | case 2: |
460 | layer = 2; | 460 | layer = 2; |
461 | break; | 461 | break; |
462 | case 3: | 462 | case 3: |
463 | layer = 1; | 463 | layer = 1; |
464 | break; | 464 | break; |
465 | default: | 465 | default: |
466 | goto restart; | 466 | goto restart; |
467 | } | 467 | } |
468 | 468 | ||
469 | /* Bitrate */ | 469 | /* Bitrate */ |
@@ -488,24 +488,27 @@ static int getsonglength(int fd, struct mp3entry *entry) | |||
488 | 488 | ||
489 | /* Calculate bytes per frame, calculation depends on layer */ | 489 | /* Calculate bytes per frame, calculation depends on layer */ |
490 | switch(layer) { | 490 | switch(layer) { |
491 | case 1: | 491 | case 1: |
492 | bpf = bitrate_table[version - 1][layer - 1][bitindex]; | 492 | bpf = bitrate_table[version - 1][layer - 1][bitindex]; |
493 | bpf *= 48000; | 493 | bpf *= 48000; |
494 | bpf /= freqtab[version-1][freqindex] << (version - 1); | 494 | bpf /= freqtab[version-1][freqindex] << (version - 1); |
495 | break; | 495 | break; |
496 | case 2: | 496 | case 2: |
497 | case 3: | 497 | case 3: |
498 | bpf = bitrate_table[version - 1][layer - 1][bitindex]; | 498 | bpf = bitrate_table[version - 1][layer - 1][bitindex]; |
499 | bpf *= 144000; | 499 | bpf *= 144000; |
500 | bpf /= freqtab[version-1][freqindex] << (version - 1); | 500 | bpf /= freqtab[version-1][freqindex] << (version - 1); |
501 | break; | 501 | break; |
502 | default: | 502 | default: |
503 | bpf = 1; | 503 | bpf = 1; |
504 | } | 504 | } |
505 | 505 | ||
506 | /* Calculate time per frame */ | 506 | /* Calculate time per frame */ |
507 | tpf = bs[layer] / (freqtab[version-1][freqindex] << (version - 1)); | 507 | tpf = bs[layer] / (freqtab[version-1][freqindex] << (version - 1)); |
508 | 508 | ||
509 | entry->bpf = bpf; | ||
510 | entry->tpf = tpf; | ||
511 | |||
509 | /* OK, we have found a frame. Let's see if it has a Xing header */ | 512 | /* OK, we have found a frame. Let's see if it has a Xing header */ |
510 | if(read(fd, frame, sizeof frame) < 0) | 513 | if(read(fd, frame, sizeof frame) < 0) |
511 | return -1; | 514 | return -1; |
@@ -535,21 +538,26 @@ static int getsonglength(int fd, struct mp3entry *entry) | |||
535 | /* Yes, it is a VBR file */ | 538 | /* Yes, it is a VBR file */ |
536 | entry->vbr = true; | 539 | entry->vbr = true; |
537 | 540 | ||
538 | if (xing[7] & 0x01) /* Is the frame count there? */ | 541 | if (entry->vbrflags & VBR_FRAMES_FLAG) /* Is the frame count there? */ |
539 | { | 542 | { |
540 | int framecount = (xing[8] << 24) | (xing[9] << 16) | | 543 | int framecount = (xing[8] << 24) | (xing[9] << 16) | |
541 | (xing[10] << 8) | xing[11]; | 544 | (xing[10] << 8) | xing[11]; |
542 | 545 | ||
543 | filetime = framecount * tpf; | 546 | filetime = framecount * tpf; |
544 | } | 547 | } |
545 | if (xing[7] & 0x02) /* is byte count there? */ | 548 | |
549 | if (entry->vbrflags & VBR_BYTES_FLAG) /* is byte count there? */ | ||
546 | { | 550 | { |
547 | int bytecount = (xing[12] << 24) | (xing[13] << 16) | | 551 | int bytecount = (xing[12] << 24) | (xing[13] << 16) | |
548 | (xing[14] << 8) | xing[15]; | 552 | (xing[14] << 8) | xing[15]; |
549 | 553 | ||
550 | bitrate = bytecount * 8 / filetime; | 554 | bitrate = bytecount * 8 / filetime; |
551 | } | 555 | } |
552 | /* We don't care about the TOC just yet. Maybe another time. */ | 556 | |
557 | if (entry->vbrflags & VBR_TOC_FLAG) /* is table-of-contents there? */ | ||
558 | { | ||
559 | memcpy( entry->toc, xing+16, 100 ); | ||
560 | } | ||
553 | } | 561 | } |
554 | 562 | ||
555 | entry->bitrate = bitrate; | 563 | 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 { | |||
29 | int tracknum; | 29 | int tracknum; |
30 | int version; | 30 | int version; |
31 | int layer; | 31 | int layer; |
32 | bool vbr; | ||
33 | unsigned int bitrate; | 32 | unsigned int bitrate; |
34 | unsigned int frequency; | 33 | unsigned int frequency; |
35 | unsigned int id3v2len; | 34 | unsigned int id3v2len; |
@@ -37,12 +36,23 @@ struct mp3entry { | |||
37 | unsigned int filesize; /* in bytes */ | 36 | unsigned int filesize; /* in bytes */ |
38 | unsigned int length; /* song length */ | 37 | unsigned int length; /* song length */ |
39 | unsigned int elapsed; /* ms played */ | 38 | unsigned int elapsed; /* ms played */ |
39 | long bpf; /* bytes per frame */ | ||
40 | long tpf; /* time per frame */ | ||
41 | |||
42 | /* Xing VBR fields */ | ||
43 | bool vbr; | ||
44 | unsigned char vbrflags; | ||
45 | unsigned char toc[100];/* table of contents */ | ||
40 | 46 | ||
41 | /* these following two fields are used for local buffering */ | 47 | /* these following two fields are used for local buffering */ |
42 | char id3v2buf[300]; | 48 | char id3v2buf[300]; |
43 | char id3v1buf[3][32]; | 49 | char id3v1buf[3][32]; |
44 | }; | 50 | }; |
45 | 51 | ||
52 | #define VBR_FRAMES_FLAG 0x01 | ||
53 | #define VBR_BYTES_FLAG 0x02 | ||
54 | #define VBR_TOC_FLAG 0x04 | ||
55 | |||
46 | bool mp3info(struct mp3entry *entry, char *filename); | 56 | bool mp3info(struct mp3entry *entry, char *filename); |
47 | 57 | ||
48 | #endif | 58 | #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 @@ | |||
47 | #define MPEG_RESUME 4 | 47 | #define MPEG_RESUME 4 |
48 | #define MPEG_NEXT 5 | 48 | #define MPEG_NEXT 5 |
49 | #define MPEG_PREV 6 | 49 | #define MPEG_PREV 6 |
50 | #define MPEG_FF_REWIND 7 | ||
50 | #define MPEG_NEED_DATA 100 | 51 | #define MPEG_NEED_DATA 100 |
51 | #define MPEG_SWAP_DATA 101 | 52 | #define MPEG_SWAP_DATA 101 |
52 | #define MPEG_TRACK_CHANGE 102 | 53 | #define MPEG_TRACK_CHANGE 102 |
@@ -216,6 +217,22 @@ static void remove_current_tag(void) | |||
216 | } | 217 | } |
217 | } | 218 | } |
218 | 219 | ||
220 | static void remove_all_non_current_tags(void) | ||
221 | { | ||
222 | int i = (tag_read_idx+1) & MAX_ID3_TAGS_MASK; | ||
223 | |||
224 | while (i != tag_write_idx) | ||
225 | { | ||
226 | id3tags[i]->used = false; | ||
227 | id3tags[i] = NULL; | ||
228 | |||
229 | i = (i+1) & MAX_ID3_TAGS_MASK; | ||
230 | } | ||
231 | |||
232 | tag_write_idx = (tag_read_idx+1) & MAX_ID3_TAGS_MASK; | ||
233 | debug_tags(); | ||
234 | } | ||
235 | |||
219 | static void remove_all_tags(void) | 236 | static void remove_all_tags(void) |
220 | { | 237 | { |
221 | int i; | 238 | int i; |
@@ -823,6 +840,112 @@ static void mpeg_thread(void) | |||
823 | break; | 840 | break; |
824 | } | 841 | } |
825 | 842 | ||
843 | case MPEG_FF_REWIND: { | ||
844 | struct mp3entry *id3 = mpeg_current_track(); | ||
845 | int newtime = id3->elapsed + (int)ev.data; | ||
846 | int curpos, newpos, diffpos; | ||
847 | DEBUGF("MPEG_FF_REWIND\n"); | ||
848 | |||
849 | if (id3->vbr && (id3->vbrflags & VBR_TOC_FLAG)) | ||
850 | { | ||
851 | /* Use the TOC to find the new position */ | ||
852 | int percent = (newtime*100)/id3->length; | ||
853 | int curtoc, nexttoc, nextpos, remainder; | ||
854 | |||
855 | if (percent > 99) | ||
856 | percent = 99; | ||
857 | |||
858 | curtoc = id3->toc[percent]; | ||
859 | |||
860 | if (percent < 99) | ||
861 | nexttoc = id3->toc[percent+1]; | ||
862 | else | ||
863 | nexttoc = 256; | ||
864 | |||
865 | newpos = (curtoc*id3->filesize)/256; | ||
866 | |||
867 | /* Use the remainder to get a more accurate position */ | ||
868 | nextpos = (nexttoc*id3->filesize)/256; | ||
869 | remainder = (newtime*10000)/id3->length - (percent*100); | ||
870 | newpos += ((nextpos-newpos)*remainder)/100; | ||
871 | } | ||
872 | else if (id3->bpf && id3->tpf) | ||
873 | newpos = (newtime*id3->bpf)/id3->tpf; | ||
874 | else | ||
875 | /* Not enough information to FF/Rewind */ | ||
876 | break; | ||
877 | |||
878 | newpos = newpos & ~1; | ||
879 | curpos = lseek(mpeg_file, 0, SEEK_CUR); | ||
880 | |||
881 | if (num_tracks_in_memory() > 1) | ||
882 | { | ||
883 | /* We have started loading other tracks that need to be | ||
884 | accounted for */ | ||
885 | int i = tag_read_idx; | ||
886 | int j = tag_write_idx - 1; | ||
887 | |||
888 | if (j < 0) | ||
889 | j = MAX_ID3_TAGS - 1; | ||
890 | |||
891 | while (i != j) | ||
892 | { | ||
893 | curpos += id3tags[i]->id3.filesize; | ||
894 | i = (i+1) & MAX_ID3_TAGS_MASK; | ||
895 | } | ||
896 | } | ||
897 | |||
898 | diffpos = curpos - newpos; | ||
899 | |||
900 | #warning "Borde inte mp3buflen vara mp3buf_write?" | ||
901 | if(diffpos >= 0 && diffpos < mp3buflen) | ||
902 | { | ||
903 | /* We are changing to a position that's already in | ||
904 | memory */ | ||
905 | mp3buf_read = mp3buf_write - diffpos; | ||
906 | if (mp3buf_read < 0) | ||
907 | { | ||
908 | mp3buf_read += mp3buflen; | ||
909 | } | ||
910 | |||
911 | playing = true; | ||
912 | last_dma_tick = current_tick; | ||
913 | init_dma(); | ||
914 | start_dma(); | ||
915 | } | ||
916 | else | ||
917 | { | ||
918 | /* Move to the new position in the file and start | ||
919 | loading data */ | ||
920 | reset_mp3_buffer(); | ||
921 | |||
922 | if (num_tracks_in_memory() > 1) | ||
923 | { | ||
924 | /* We have to reload the current track */ | ||
925 | close(mpeg_file); | ||
926 | remove_all_non_current_tags(); | ||
927 | |||
928 | mpeg_file = open(id3->path, O_RDONLY); | ||
929 | if (mpeg_file < 0) | ||
930 | break; | ||
931 | } | ||
932 | |||
933 | if(-1 == lseek(mpeg_file, newpos, SEEK_SET)) | ||
934 | break; | ||
935 | |||
936 | filling = true; | ||
937 | queue_post(&mpeg_queue, MPEG_NEED_DATA, 0); | ||
938 | |||
939 | /* Tell the file loading code that we want to start playing | ||
940 | as soon as we have some data */ | ||
941 | play_pending = true; | ||
942 | } | ||
943 | |||
944 | id3->elapsed = newtime; | ||
945 | |||
946 | break; | ||
947 | } | ||
948 | |||
826 | case MPEG_SWAP_DATA: | 949 | case MPEG_SWAP_DATA: |
827 | free_space_left = mp3buf_write - mp3buf_swapwrite; | 950 | free_space_left = mp3buf_write - mp3buf_swapwrite; |
828 | 951 | ||
@@ -1128,6 +1251,15 @@ void mpeg_prev(void) | |||
1128 | #endif | 1251 | #endif |
1129 | } | 1252 | } |
1130 | 1253 | ||
1254 | void mpeg_ff_rewind(int change) | ||
1255 | { | ||
1256 | #ifndef SIMULATOR | ||
1257 | queue_post(&mpeg_queue, MPEG_FF_REWIND, (void *)change); | ||
1258 | #else | ||
1259 | (void)change; | ||
1260 | #endif | ||
1261 | } | ||
1262 | |||
1131 | bool mpeg_is_playing(void) | 1263 | bool mpeg_is_playing(void) |
1132 | { | 1264 | { |
1133 | return playing || play_pending; | 1265 | 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); | |||
28 | void mpeg_resume(void); | 28 | void mpeg_resume(void); |
29 | void mpeg_next(void); | 29 | void mpeg_next(void); |
30 | void mpeg_prev(void); | 30 | void mpeg_prev(void); |
31 | void mpeg_ff_rewind(int change); | ||
31 | bool mpeg_is_playing(void); | 32 | bool mpeg_is_playing(void); |
32 | void mpeg_sound_set(int setting, int value); | 33 | void mpeg_sound_set(int setting, int value); |
33 | int mpeg_sound_min(int setting); | 34 | int mpeg_sound_min(int setting); |