diff options
Diffstat (limited to 'apps/screens.c')
-rw-r--r-- | apps/screens.c | 181 |
1 files changed, 177 insertions, 4 deletions
diff --git a/apps/screens.c b/apps/screens.c index 5fa92f5fbd..d341fa8eef 100644 --- a/apps/screens.c +++ b/apps/screens.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <stdbool.h> | 22 | #include <stdbool.h> |
23 | #include <string.h> | 23 | #include <string.h> |
24 | #include <stdio.h> | 24 | #include <stdio.h> |
25 | #include <stdlib.h> | ||
25 | #include "backlight.h" | 26 | #include "backlight.h" |
26 | #include "action.h" | 27 | #include "action.h" |
27 | #include "lcd.h" | 28 | #include "lcd.h" |
@@ -81,6 +82,7 @@ int mmc_remove_request(void) | |||
81 | } | 82 | } |
82 | } | 83 | } |
83 | #endif | 84 | #endif |
85 | #include "ctype.h" | ||
84 | 86 | ||
85 | /* the charging screen is only used for archos targets */ | 87 | /* the charging screen is only used for archos targets */ |
86 | #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) && defined(CPU_SH) | 88 | #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) && defined(CPU_SH) |
@@ -643,14 +645,102 @@ struct id3view_info { | |||
643 | int info_id[ARRAYLEN(id3_headers)]; | 645 | int info_id[ARRAYLEN(id3_headers)]; |
644 | }; | 646 | }; |
645 | 647 | ||
646 | static const char* id3_get_info(int selected_item, void* data, | 648 | /* Spell out a buffer, but when successive digits are encountered, say |
647 | char *buffer, size_t buffer_len) | 649 | the whole number. Useful for some ID3 tags that usually contain a |
650 | number but are in fact free-form. */ | ||
651 | static void say_number_and_spell(char *buf, bool year_style) | ||
652 | { | ||
653 | char *ptr = buf; | ||
654 | while(*ptr) { | ||
655 | if(isdigit(*ptr)) { | ||
656 | /* parse the number */ | ||
657 | int n = atoi(ptr); | ||
658 | /* skip over digits to rest of string */ | ||
659 | while(isdigit(*++ptr)); | ||
660 | /* say the number */ | ||
661 | if(year_style) | ||
662 | talk_value(n, UNIT_DATEYEAR, true); | ||
663 | else talk_number(n, true); | ||
664 | }else{ | ||
665 | /* Spell a sequence of non-digits */ | ||
666 | char tmp, *start = ptr; | ||
667 | while(*++ptr && !isdigit(*ptr)); | ||
668 | /* temporarily truncate the string here */ | ||
669 | tmp = *ptr; | ||
670 | *ptr = '\0'; | ||
671 | talk_spell(start, true); | ||
672 | *ptr = tmp; /* restore string */ | ||
673 | } | ||
674 | } | ||
675 | } | ||
676 | |||
677 | /* Say a replaygain ID3 value from its text form */ | ||
678 | static void say_gain(char *buf) | ||
679 | { | ||
680 | /* Expected form is "-5.74 dB". We'll try to parse out the number | ||
681 | until the dot, say it (forcing the + sign), then say dot and | ||
682 | spell the following numbers, and then say the decibel unit. */ | ||
683 | char *ptr = buf; | ||
684 | if(*ptr == '-' || *ptr == '+') | ||
685 | /* skip sign */ | ||
686 | ++ptr; | ||
687 | /* See if we can parse out a number. */ | ||
688 | if(isdigit(*ptr)) { | ||
689 | char tmp; | ||
690 | /* skip successive digits */ | ||
691 | while(isdigit(*++ptr)); | ||
692 | /* temporarily truncate the string here */ | ||
693 | tmp = *ptr; | ||
694 | *ptr = '\0'; | ||
695 | /* parse out the number we just skipped */ | ||
696 | talk_value(atoi(buf), UNIT_SIGNED, true); /* say the number with sign */ | ||
697 | *ptr = tmp; /* restore the string */ | ||
698 | if(*ptr == '.') { | ||
699 | /* found the dot, get fractional part */ | ||
700 | buf = ptr; | ||
701 | while (isdigit(*++ptr)); | ||
702 | while (*--ptr == '0'); | ||
703 | if (ptr > buf) { | ||
704 | tmp = *++ptr; | ||
705 | *ptr = '\0'; | ||
706 | talk_id(LANG_POINT, true); | ||
707 | while (*++buf == '0') | ||
708 | talk_id(VOICE_ZERO, true); | ||
709 | talk_number(atoi(buf), true); | ||
710 | *ptr = tmp; | ||
711 | } | ||
712 | ptr = buf; | ||
713 | while (isdigit(*++ptr)); | ||
714 | } | ||
715 | buf = ptr; | ||
716 | if(strlen(buf) >2 && !strcmp(buf+strlen(buf)-2, "dB")) { | ||
717 | /* String does end with "dB" */ | ||
718 | /* point to that "dB" */ | ||
719 | ptr = buf+strlen(buf)-2; | ||
720 | /* backup any spaces */ | ||
721 | while (ptr >buf && ptr[-1] == ' ') | ||
722 | --ptr; | ||
723 | if (ptr > buf) | ||
724 | talk_spell(buf, true); | ||
725 | else talk_id(VOICE_DB, true); /* say the dB unit */ | ||
726 | }else /* doesn't end with dB, just spell everything after the | ||
727 | number of dot. */ | ||
728 | talk_spell(buf, true); | ||
729 | }else /* we didn't find a number, just spell everything */ | ||
730 | talk_spell(buf, true); | ||
731 | } | ||
732 | |||
733 | static const char * id3_get_or_speak_info(int selected_item, void* data, | ||
734 | char *buffer, size_t buffer_len, | ||
735 | bool say_it) | ||
648 | { | 736 | { |
649 | struct id3view_info *info = (struct id3view_info*)data; | 737 | struct id3view_info *info = (struct id3view_info*)data; |
650 | struct mp3entry* id3 =info->id3; | 738 | struct mp3entry* id3 =info->id3; |
651 | int info_no=selected_item/2; | 739 | int info_no=selected_item/2; |
652 | if(!(selected_item%2)) | 740 | if(!(selected_item%2)) |
653 | {/* header */ | 741 | {/* header */ |
742 | if(say_it) | ||
743 | talk_id(id3_headers[info->info_id[info_no]], false); | ||
654 | snprintf(buffer, buffer_len, | 744 | snprintf(buffer, buffer_len, |
655 | "[%s]", str(id3_headers[info->info_id[info_no]])); | 745 | "[%s]", str(id3_headers[info->info_id[info_no]])); |
656 | return buffer; | 746 | return buffer; |
@@ -663,35 +753,57 @@ static const char* id3_get_info(int selected_item, void* data, | |||
663 | { | 753 | { |
664 | case LANG_ID3_TITLE: | 754 | case LANG_ID3_TITLE: |
665 | val=id3->title; | 755 | val=id3->title; |
756 | if(say_it && val) | ||
757 | talk_spell(val, true); | ||
666 | break; | 758 | break; |
667 | case LANG_ID3_ARTIST: | 759 | case LANG_ID3_ARTIST: |
668 | val=id3->artist; | 760 | val=id3->artist; |
761 | if(say_it && val) | ||
762 | talk_spell(val, true); | ||
669 | break; | 763 | break; |
670 | case LANG_ID3_ALBUM: | 764 | case LANG_ID3_ALBUM: |
671 | val=id3->album; | 765 | val=id3->album; |
766 | if(say_it && val) | ||
767 | talk_spell(val, true); | ||
672 | break; | 768 | break; |
673 | case LANG_ID3_ALBUMARTIST: | 769 | case LANG_ID3_ALBUMARTIST: |
674 | val=id3->albumartist; | 770 | val=id3->albumartist; |
771 | if(say_it && val) | ||
772 | talk_spell(val, true); | ||
675 | break; | 773 | break; |
676 | case LANG_ID3_GROUPING: | 774 | case LANG_ID3_GROUPING: |
677 | val=id3->grouping; | 775 | val=id3->grouping; |
776 | if(say_it && val) | ||
777 | talk_spell(val, true); | ||
678 | break; | 778 | break; |
679 | case LANG_ID3_DISCNUM: | 779 | case LANG_ID3_DISCNUM: |
680 | if (id3->disc_string) | 780 | if (id3->disc_string) |
781 | { | ||
681 | val = id3->disc_string; | 782 | val = id3->disc_string; |
783 | if(say_it) | ||
784 | say_number_and_spell(val, true); | ||
785 | } | ||
682 | else if (id3->discnum) | 786 | else if (id3->discnum) |
683 | { | 787 | { |
684 | snprintf(buffer, buffer_len, "%d", id3->discnum); | 788 | snprintf(buffer, buffer_len, "%d", id3->discnum); |
685 | val = buffer; | 789 | val = buffer; |
790 | if(say_it) | ||
791 | talk_number(id3->discnum, true); | ||
686 | } | 792 | } |
687 | break; | 793 | break; |
688 | case LANG_ID3_TRACKNUM: | 794 | case LANG_ID3_TRACKNUM: |
689 | if (id3->track_string) | 795 | if (id3->track_string) |
796 | { | ||
690 | val = id3->track_string; | 797 | val = id3->track_string; |
798 | if(say_it) | ||
799 | say_number_and_spell(val, true); | ||
800 | } | ||
691 | else if (id3->tracknum) | 801 | else if (id3->tracknum) |
692 | { | 802 | { |
693 | snprintf(buffer, buffer_len, "%d", id3->tracknum); | 803 | snprintf(buffer, buffer_len, "%d", id3->tracknum); |
694 | val = buffer; | 804 | val = buffer; |
805 | if(say_it) | ||
806 | talk_number(id3->tracknum, true); | ||
695 | } | 807 | } |
696 | break; | 808 | break; |
697 | case LANG_ID3_COMMENT: | 809 | case LANG_ID3_COMMENT: |
@@ -699,62 +811,119 @@ static const char* id3_get_info(int selected_item, void* data, | |||
699 | return NULL; | 811 | return NULL; |
700 | snprintf(buffer, buffer_len, "%s", id3->comment); | 812 | snprintf(buffer, buffer_len, "%s", id3->comment); |
701 | val=buffer; | 813 | val=buffer; |
814 | if(say_it && val) | ||
815 | talk_spell(val, true); | ||
702 | break; | 816 | break; |
703 | case LANG_ID3_GENRE: | 817 | case LANG_ID3_GENRE: |
704 | val = id3->genre_string; | 818 | val = id3->genre_string; |
819 | if(say_it && val) | ||
820 | talk_spell(val, true); | ||
705 | break; | 821 | break; |
706 | case LANG_ID3_YEAR: | 822 | case LANG_ID3_YEAR: |
707 | if (id3->year_string) | 823 | if (id3->year_string) |
824 | { | ||
708 | val = id3->year_string; | 825 | val = id3->year_string; |
826 | if(say_it && val) | ||
827 | say_number_and_spell(val, true); | ||
828 | } | ||
709 | else if (id3->year) | 829 | else if (id3->year) |
710 | { | 830 | { |
711 | snprintf(buffer, buffer_len, "%d", id3->year); | 831 | snprintf(buffer, buffer_len, "%d", id3->year); |
712 | val = buffer; | 832 | val = buffer; |
833 | if(say_it) | ||
834 | talk_value(id3->year, UNIT_DATEYEAR, true); | ||
713 | } | 835 | } |
714 | break; | 836 | break; |
715 | case LANG_ID3_LENGTH: | 837 | case LANG_ID3_LENGTH: |
716 | format_time(buffer, buffer_len, id3->length); | 838 | format_time(buffer, buffer_len, id3->length); |
717 | val=buffer; | 839 | val=buffer; |
840 | if(say_it) | ||
841 | talk_value(id3->length /1000, UNIT_TIME, true); | ||
718 | break; | 842 | break; |
719 | case LANG_ID3_PLAYLIST: | 843 | case LANG_ID3_PLAYLIST: |
720 | snprintf(buffer, buffer_len, "%d/%d", | 844 | snprintf(buffer, buffer_len, "%d/%d", |
721 | playlist_get_display_index(), playlist_amount()); | 845 | playlist_get_display_index(), playlist_amount()); |
722 | val=buffer; | 846 | val=buffer; |
847 | if(say_it) | ||
848 | { | ||
849 | talk_number(playlist_get_display_index(), true); | ||
850 | talk_id(VOICE_OF, true); | ||
851 | talk_number(playlist_amount(), true); | ||
852 | } | ||
723 | break; | 853 | break; |
724 | case LANG_ID3_BITRATE: | 854 | case LANG_ID3_BITRATE: |
725 | snprintf(buffer, buffer_len, "%d kbps%s", id3->bitrate, | 855 | snprintf(buffer, buffer_len, "%d kbps%s", id3->bitrate, |
726 | id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) ""); | 856 | id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) ""); |
727 | val=buffer; | 857 | val=buffer; |
858 | if(say_it) | ||
859 | { | ||
860 | talk_value(id3->bitrate, UNIT_KBIT, true); | ||
861 | if(id3->vbr) | ||
862 | talk_id(LANG_ID3_VBR, true); | ||
863 | } | ||
728 | break; | 864 | break; |
729 | case LANG_ID3_FREQUENCY: | 865 | case LANG_ID3_FREQUENCY: |
730 | snprintf(buffer, buffer_len, "%ld Hz", id3->frequency); | 866 | snprintf(buffer, buffer_len, "%ld Hz", id3->frequency); |
731 | val=buffer; | 867 | val=buffer; |
868 | if(say_it) | ||
869 | talk_value(id3->frequency, UNIT_HERTZ, true); | ||
732 | break; | 870 | break; |
733 | #if CONFIG_CODEC == SWCODEC | 871 | #if CONFIG_CODEC == SWCODEC |
734 | case LANG_ID3_TRACK_GAIN: | 872 | case LANG_ID3_TRACK_GAIN: |
735 | replaygain_itoa(buffer, buffer_len, id3->track_level); | 873 | replaygain_itoa(buffer, buffer_len, id3->track_level); |
736 | val=(id3->track_level) ? buffer : NULL; /* only show level!=0 */ | 874 | val=(id3->track_level) ? buffer : NULL; /* only show level!=0 */ |
875 | if(say_it && val) | ||
876 | say_gain(val); | ||
737 | break; | 877 | break; |
738 | case LANG_ID3_ALBUM_GAIN: | 878 | case LANG_ID3_ALBUM_GAIN: |
739 | replaygain_itoa(buffer, buffer_len, id3->album_level); | 879 | replaygain_itoa(buffer, buffer_len, id3->album_level); |
740 | val=(id3->album_level) ? buffer : NULL; /* only show level!=0 */ | 880 | val=(id3->album_level) ? buffer : NULL; /* only show level!=0 */ |
881 | if(say_it && val) | ||
882 | say_gain(val); | ||
741 | break; | 883 | break; |
742 | #endif | 884 | #endif |
743 | case LANG_ID3_PATH: | 885 | case LANG_ID3_PATH: |
744 | val=id3->path; | 886 | val=id3->path; |
887 | if(say_it && val) | ||
888 | talk_fullpath(val, true); | ||
745 | break; | 889 | break; |
746 | case LANG_ID3_COMPOSER: | 890 | case LANG_ID3_COMPOSER: |
747 | val=id3->composer; | 891 | val=id3->composer; |
892 | if(say_it && val) | ||
893 | talk_spell(val, true); | ||
748 | break; | 894 | break; |
749 | case LANG_FILESIZE: /* not LANG_ID3_FILESIZE because the string is shared */ | 895 | case LANG_FILESIZE: /* not LANG_ID3_FILESIZE because the string is shared */ |
750 | output_dyn_value(buffer, buffer_len, id3->filesize, byte_units, 4, true); | 896 | output_dyn_value(buffer, buffer_len, id3->filesize, byte_units, 4, true); |
751 | val=buffer; | 897 | val=buffer; |
898 | if(say_it && val) | ||
899 | output_dyn_value(NULL, 0, id3->filesize, byte_units, true); | ||
752 | break; | 900 | break; |
753 | } | 901 | } |
902 | if((!val || !*val) && say_it) | ||
903 | talk_id(LANG_ID3_NO_INFO, true); | ||
754 | return val && *val ? val : NULL; | 904 | return val && *val ? val : NULL; |
755 | } | 905 | } |
756 | } | 906 | } |
757 | 907 | ||
908 | /* gui_synclist callback */ | ||
909 | static const char* id3_get_info(int selected_item, void* data, | ||
910 | char *buffer, size_t buffer_len) | ||
911 | { | ||
912 | return id3_get_or_speak_info(selected_item, data, buffer, | ||
913 | buffer_len, false); | ||
914 | } | ||
915 | |||
916 | static int id3_speak_item(int selected_item, void* data) | ||
917 | { | ||
918 | char buffer[MAX_PATH]; | ||
919 | selected_item &= ~1; /* Make sure it's even, to indicate the header */ | ||
920 | /* say field name */ | ||
921 | id3_get_or_speak_info(selected_item, data, buffer, MAX_PATH, true); | ||
922 | /* and field value */ | ||
923 | id3_get_or_speak_info(selected_item+1, data, buffer, MAX_PATH, true); | ||
924 | return 0; | ||
925 | } | ||
926 | |||
758 | bool browse_id3(void) | 927 | bool browse_id3(void) |
759 | { | 928 | { |
760 | struct gui_synclist id3_lists; | 929 | struct gui_synclist id3_lists; |
@@ -775,11 +944,15 @@ bool browse_id3(void) | |||
775 | } | 944 | } |
776 | 945 | ||
777 | gui_synclist_init(&id3_lists, &id3_get_info, &info, true, 2, NULL); | 946 | gui_synclist_init(&id3_lists, &id3_get_info, &info, true, 2, NULL); |
947 | if(global_settings.talk_menu) | ||
948 | gui_synclist_set_voice_callback(&id3_lists, id3_speak_item); | ||
778 | gui_synclist_set_nb_items(&id3_lists, info.count*2); | 949 | gui_synclist_set_nb_items(&id3_lists, info.count*2); |
779 | gui_synclist_draw(&id3_lists); | 950 | gui_synclist_draw(&id3_lists); |
951 | gui_synclist_speak_item(&id3_lists); | ||
780 | while (true) { | 952 | while (true) { |
781 | key = get_action(CONTEXT_LIST,HZ/2); | 953 | if(!list_do_action(CONTEXT_LIST,HZ/2, |
782 | if(!gui_synclist_do_button(&id3_lists, &key,LIST_WRAP_UNLESS_HELD)) | 954 | &id3_lists, &key,LIST_WRAP_UNLESS_HELD) |
955 | && key!=ACTION_NONE && key!=ACTION_UNKNOWN) | ||
783 | { | 956 | { |
784 | if (key == ACTION_STD_OK || key == ACTION_STD_CANCEL) | 957 | if (key == ACTION_STD_OK || key == ACTION_STD_CANCEL) |
785 | { | 958 | { |