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.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.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 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
135static void ff_rewind(void)
136{
137 set_int("[FF/Rewind Step Size]", "s", &global_settings.ff_rewind,
138 NULL, 1, 1, 999 );
139}
140
135void settings_menu(void) 141void 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
62bool keys_locked = false; 62bool keys_locked = false;
63static bool ff_rewind = false;
63 64
64static void draw_screen(struct mp3entry* id3) 65static 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
295static 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
294void display_keylock_text(bool locked) 328void 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
46bool mp3info(struct mp3entry *entry, char *filename); 56bool 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
220static 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
219static void remove_all_tags(void) 236static 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
1254void 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
1131bool mpeg_is_playing(void) 1263bool 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);
28void mpeg_resume(void); 28void mpeg_resume(void);
29void mpeg_next(void); 29void mpeg_next(void);
30void mpeg_prev(void); 30void mpeg_prev(void);
31void mpeg_ff_rewind(int change);
31bool mpeg_is_playing(void); 32bool mpeg_is_playing(void);
32void mpeg_sound_set(int setting, int value); 33void mpeg_sound_set(int setting, int value);
33int mpeg_sound_min(int setting); 34int mpeg_sound_min(int setting);