Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

talk.c clean up

Change-Id: Ie06859c80e9ca14bf3e4a23b82d3d1c76263b2b8

+148 -150
+141 -150
apps/talk.c
··· 97 97 #define MAX_CLIP_BUFFER_SIZE (1<<30) 98 98 #endif 99 99 #define THUMBNAIL_RESERVE (50000) 100 - 100 + #define NULL_TERMINATED ((size_t)-1) 101 101 /* Multiple thumbnails can be loaded back-to-back in this buffer. */ 102 102 static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in 103 103 thumbnail buffer */ ··· 118 118 static bool talk_initialized; /* true if talk_init has been called */ 119 119 static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */ 120 120 static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */ 121 + 121 122 /* size of the voice data in the voice file and the actually allocated buffer 122 123 * for it. voicebuf_size is always smaller or equal to voicefile_size */ 123 124 static unsigned long voicefile_size, voicebuf_size; ··· 306 307 unsigned i; 307 308 int oldest = 0; 308 309 bool thumb = false; 309 - long age, now; 310 + long age, now, next_age; 310 311 struct clip_entry* clipbuf; 311 312 struct clip_cache_metadata *cc = buflib_get_data(&clip_ctx, metadata_table_handle); 312 313 for(age = i = 0, now = current_tick; i < max_clips; i++) 313 314 { 314 315 if (cc[i].handle) 315 316 { 316 - if (thumb && cc[i].voice_id == VOICEONLY_DELIMITER && (now - cc[i].tick) > age) 317 + next_age = (now - cc[i].tick); 318 + if (thumb && cc[i].voice_id == VOICEONLY_DELIMITER && next_age > age) 317 319 { 318 320 /* thumb clips are freed first */ 319 321 age = now - cc[i].tick; ··· 323 325 { 324 326 if (cc[i].voice_id == VOICEONLY_DELIMITER) 325 327 { 326 - age = now - cc[i].tick; 328 + age = next_age; 327 329 oldest = i; 328 330 thumb = true; 329 331 } 330 - else if ((now - cc[i].tick) > age && cc[i].voice_id != VOICE_PAUSE) 332 + else if (next_age > age && cc[i].voice_id != VOICE_PAUSE) 331 333 { 332 334 /* find the last-used clip but never consider silence */ 333 - age = now - cc[i].tick; 335 + age = next_age; 334 336 oldest = i; 335 337 } 336 338 } ··· 347 349 } 348 350 return oldest; 349 351 } 350 - 351 352 352 353 /* common code for load_initial_clips() and get_clip() */ 353 354 static void add_cache_entry(int clip_handle, int table_index, int id) ··· 549 550 index_handle = core_free(index_handle); 550 551 return false; 551 552 } 553 + 552 554 static inline int load_voicefile_failure(int fd) 553 555 { 554 556 if (fd >= 0) 555 557 close(fd); 556 558 return -1; 557 559 } 560 + 558 561 /* load the voice file into the mp3 buffer */ 559 562 static bool load_voicefile_index(int fd) 560 563 { ··· 719 722 talk_queue_unlock(); 720 723 } 721 724 725 + /***************** Private routines *****************/ 726 + 727 + /* return if a voice codec is required or not */ 728 + static bool talk_voice_required(void) 729 + { 730 + return (has_voicefile) /* Voice file is available */ 731 + || (global_settings.talk_dir_clip) /* Thumbnail clips are required */ 732 + || (global_settings.talk_file_clip); 733 + } 734 + 735 + static bool talk_is_disabled(void) 736 + { 737 + if (talk_temp_disable_count > 0 || (!check_audio_status())) 738 + return true; 739 + return false; 740 + } 741 + 742 + static void do_enqueue(bool enqueue) 743 + { 744 + if (!enqueue) 745 + talk_shutup(); /* cut off all the pending stuff */ 746 + } 747 + 748 + /* spell a string */ 749 + static int _talk_spell(const char* spell, size_t len, bool enqueue) 750 + { 751 + char c; /* currently processed char */ 752 + 753 + if (talk_is_disabled()) 754 + return -1; 755 + 756 + do_enqueue(enqueue); /* cut off all the pending stuff */ 757 + 758 + size_t len0 = len - 1; 759 + 760 + while ((c = *spell++) != '\0' && len0-- < len) 761 + { 762 + /* if this grows into too many cases, I should use a table */ 763 + if (c >= 'A' && c <= 'Z') 764 + talk_id(VOICE_CHAR_A + c - 'A', true); 765 + else if (c >= 'a' && c <= 'z') 766 + talk_id(VOICE_CHAR_A + c - 'a', true); 767 + else if (c >= '0' && c <= '9') 768 + talk_id(VOICE_ZERO + c - '0', true); 769 + else if (c == '-') 770 + talk_id(VOICE_MINUS, true); 771 + else if (c == '+') 772 + talk_id(VOICE_PLUS, true); 773 + else if (c == '.') 774 + talk_id(VOICE_DOT, true); 775 + else if (c == ' ') 776 + talk_id(VOICE_PAUSE, true); 777 + else if (c == '/') 778 + talk_id(VOICE_CHAR_SLASH, true); 779 + } 780 + return 0; 781 + } 782 + 783 + static int talk_spell_basename(const char *path, 784 + const long *prefix_ids, bool enqueue) 785 + { 786 + if(prefix_ids) 787 + { 788 + talk_idarray(prefix_ids, enqueue); 789 + enqueue = true; 790 + } 791 + const char *basename; 792 + size_t len = path_basename(path, &basename); 793 + 794 + return _talk_spell(basename, len, enqueue); 795 + } 796 + 797 + /* Say year like "nineteen ninety nine" instead of "one thousand 9 798 + hundred ninety nine". */ 799 + static int talk_year(long year, bool enqueue) 800 + { 801 + int rem; 802 + if(year < 1100 || (year >=2000 && year < 2100)) 803 + /* just say it as a regular number */ 804 + return talk_number(year, enqueue); 805 + /* Say century */ 806 + talk_number(year/100, enqueue); 807 + rem = year%100; 808 + if(rem == 0) 809 + /* as in 1900 */ 810 + return talk_id(VOICE_HUNDRED, true); 811 + if(rem <10) 812 + /* as in 1905 */ 813 + talk_id(VOICE_ZERO, true); 814 + /* sub-century year */ 815 + return talk_number(rem, true); 816 + } 817 + 722 818 /***************** Public routines *****************/ 723 819 724 820 /* stop the playback and the pending clips */ ··· 746 842 struct queue_entry *qe; 747 843 int queue_level; 748 844 749 - if (!enqueue) 750 - talk_shutup(); /* cut off all the pending stuff */ 845 + do_enqueue(enqueue); /* cut off all the pending stuff */ 846 + 751 847 /* Something is being enqueued, force_enqueue_next override is no 752 848 longer in effect. */ 753 849 force_enqueue_next = false; ··· 779 875 need_shutup = true; 780 876 781 877 return; 782 - } 783 - 784 - /* return if a voice codec is required or not */ 785 - static bool talk_voice_required(void) 786 - { 787 - return (has_voicefile) /* Voice file is available */ 788 - || (global_settings.talk_dir_clip) /* Thumbnail clips are required */ 789 - || (global_settings.talk_file_clip); 790 878 } 791 879 792 880 /***************** Public implementation *****************/ ··· 885 973 /* somebody else claims the mp3 buffer, e.g. for regular play/record */ 886 974 void talk_buffer_set_policy(int policy) 887 975 { 976 + #ifdef DEBUG 888 977 switch(policy) 889 978 { 890 979 case TALK_BUFFER_DEFAULT: ··· 892 981 case TALK_BUFFER_LOOSE: give_buffer_away = true; break; 893 982 default: DEBUGF("Ignoring unknown policy\n"); break; 894 983 } 984 + #else 985 + give_buffer_away = (policy == TALK_BUFFER_LOOSE); 986 + #endif 895 987 } 896 988 897 989 /* play a voice ID from voicefile */ ··· 903 995 bool isloaded = true; 904 996 905 997 if (!has_voicefile) 906 - return 0; /* no voicefile loaded, not an error -> pretent success */ 907 - if (talk_temp_disable_count > 0) 908 - return -1; /* talking has been disabled */ 909 - if (!check_audio_status()) 998 + return 0; /* no voicefile loaded, not an error -> pretend success */ 999 + if (talk_is_disabled()) 910 1000 return -1; 911 1001 912 1002 if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */ ··· 948 1038 949 1039 return 0; 950 1040 } 1041 + 951 1042 /* Speaks zero or more IDs (from an array). */ 952 1043 int talk_idarray(const long *ids, bool enqueue) 953 1044 { ··· 981 1072 int oldest = -1; 982 1073 983 1074 /* reload needed? */ 984 - if (talk_temp_disable_count > 0) 985 - return -1; /* talking has been disabled */ 986 - if (!check_audio_status()) 1075 + if (talk_is_disabled()) 987 1076 return -1; 988 1077 if (talk_handle <= 0 || index_handle <= 0) 989 1078 { ··· 994 1083 close(fd); 995 1084 } 996 1085 997 - if (!enqueue) 998 - /* shutup now to free the thumbnail buffer */ 999 - talk_shutup(); 1086 + do_enqueue(enqueue); /* shutup now to free the thumbnail buffer */ 1000 1087 1001 1088 fd = open(filename, O_RDONLY); 1002 1089 if (fd < 0) /* failed to open */ ··· 1045 1132 /* Play a thumbnail file */ 1046 1133 { 1047 1134 char buf[MAX_PATH]; 1135 + const char *fmt = "%s%s%s%s%s"; 1048 1136 /* Does root end with a slash */ 1049 - char *slash = (root && root[0] 1050 - && root[strlen(root)-1] != '/') ? "/" : ""; 1051 - snprintf(buf, MAX_PATH, "%s%s%s%s%s%s", 1052 - root ? root : "", slash, 1137 + if(root && root[0] && root[strlen(root)-1] != '/') 1138 + fmt = "%s/%s%s%s%s"; 1139 + snprintf(buf, MAX_PATH, fmt, 1140 + root ? root : "", 1053 1141 dir ? dir : "", dir ? "/" : "", 1054 1142 file ? file : "", 1055 1143 ext ? ext : ""); 1056 1144 return _talk_file(buf, prefix_ids, enqueue); 1057 1145 } 1058 1146 1059 - static int talk_spell_basename(const char *path, 1060 - const long *prefix_ids, bool enqueue) 1061 - { 1062 - if(prefix_ids) 1063 - { 1064 - talk_idarray(prefix_ids, enqueue); 1065 - enqueue = true; 1066 - } 1067 - char buf[MAX_PATH]; 1068 - /* Spell only the path component after the last slash */ 1069 - char *end = strmemccpy(buf, path, sizeof(buf)); 1070 - 1071 - if (!end) 1072 - return 0; 1073 - 1074 - size_t len = end - buf - 1; 1075 - if(len >1 && buf[len-1] == '/') 1076 - buf[--len] = '\0'; /* strip trailing slash */ 1077 - 1078 - char *ptr = strrchr(buf, '/'); 1079 - if(ptr && len >1) 1080 - ++ptr; 1081 - else 1082 - ptr = buf; 1083 - 1084 - return talk_spell(ptr, enqueue); 1085 - } 1086 - 1087 1147 /* Play a file's .talk thumbnail, fallback to spelling the filename, or 1088 1148 go straight to spelling depending on settings. */ 1089 1149 int talk_file_or_spell(const char *dirname, const char *filename, ··· 1095 1155 prefix_ids, enqueue) >0) 1096 1156 return 0; 1097 1157 } 1098 - if (global_settings.talk_file == 2) 1158 + if (global_settings.talk_file == TALK_SPEAK_SPELL) 1099 1159 /* Either .talk clips are disabled, or as a fallback */ 1100 1160 return talk_spell_basename(filename, prefix_ids, enqueue); 1101 1161 return 0; ··· 1112 1172 prefix_ids, enqueue) >0) 1113 1173 return 0; 1114 1174 } 1115 - if (global_settings.talk_dir == 2) 1175 + if (global_settings.talk_dir == TALK_SPEAK_SPELL) 1116 1176 /* Either .talk clips disabled or as a fallback */ 1117 1177 return talk_spell_basename(dirname, prefix_ids, enqueue); 1118 1178 return 0; ··· 1122 1182 back or going straight to spelling depending on settings. */ 1123 1183 int talk_fullpath(const char* path, bool enqueue) 1124 1184 { 1125 - if (!enqueue) 1126 - talk_shutup(); 1185 + do_enqueue(enqueue); /* cut off all the pending stuff */ 1186 + 1127 1187 if(path[0] != '/') 1128 1188 /* path ought to start with /... */ 1129 1189 return talk_spell(path, true); ··· 1152 1212 int level = 2; /* mille count */ 1153 1213 long mil = 1000000000; /* highest possible "-illion" */ 1154 1214 1155 - if (talk_temp_disable_count > 0) 1156 - return -1; /* talking has been disabled */ 1157 - if (!check_audio_status()) 1215 + if (talk_is_disabled()) 1158 1216 return -1; 1159 1217 1160 - if (!enqueue) 1161 - talk_shutup(); /* cut off all the pending stuff */ 1218 + do_enqueue(enqueue); /* cut off all the pending stuff */ 1162 1219 1163 1220 if (n==0) 1164 1221 { /* special case */ ··· 1243 1300 return 0; 1244 1301 } 1245 1302 1246 - /* Say year like "nineteen ninety nine" instead of "one thousand 9 1247 - hundred ninety nine". */ 1248 - static int talk_year(long year, bool enqueue) 1249 - { 1250 - int rem; 1251 - if(year < 1100 || (year >=2000 && year < 2100)) 1252 - /* just say it as a regular number */ 1253 - return talk_number(year, enqueue); 1254 - /* Say century */ 1255 - talk_number(year/100, enqueue); 1256 - rem = year%100; 1257 - if(rem == 0) 1258 - /* as in 1900 */ 1259 - return talk_id(VOICE_HUNDRED, true); 1260 - if(rem <10) 1261 - /* as in 1905 */ 1262 - talk_id(VOICE_ZERO, true); 1263 - /* sub-century year */ 1264 - return talk_number(rem, true); 1265 - } 1266 - 1267 1303 /* Say time duration/interval. Input is time in seconds, 1268 1304 say hours,minutes,seconds. */ 1269 1305 static int talk_time_unit(long secs, bool enqueue) ··· 1336 1372 char tbuf[8]; 1337 1373 char fmt[] = "%0nd"; 1338 1374 1339 - if (talk_temp_disable_count > 0) 1340 - return -1; /* talking has been disabled */ 1341 - if (!check_audio_status()) 1375 + if (talk_is_disabled()) 1342 1376 return -1; 1343 1377 1344 1378 /* special pronounciation for year number */ ··· 1389 1423 return 0; 1390 1424 } 1391 1425 1426 + static inline void talk_time_value(long n, int unit, bool enqueue) 1427 + { 1428 + if (n != 0) 1429 + talk_value_decimal(n, unit, 0, enqueue); 1430 + } 1431 + 1392 1432 /* Say time duration/interval. Input is time unit specifies base unit, 1393 1433 say hours,minutes,seconds, milliseconds. or any combination thereof */ 1394 1434 int talk_time_intervals(long time, int unit_idx, bool enqueue) 1395 1435 { 1396 1436 unsigned long units_in[UNIT_IDX_TIME_COUNT]; 1397 1437 1398 - if (talk_temp_disable_count > 0) 1399 - return -1; /* talking has been disabled */ 1400 - if (!check_audio_status()) 1438 + if (talk_is_disabled()) 1401 1439 return -1; 1402 1440 1403 1441 if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */ ··· 1409 1447 close(fd); 1410 1448 } 1411 1449 1412 - if (!enqueue) 1413 - talk_shutup(); /* cut off all the pending stuff */ 1450 + do_enqueue(enqueue); /* cut off all the pending stuff */ 1414 1451 1415 1452 time_split_units(unit_idx, labs(time), &units_in); 1416 1453 ··· 1421 1458 talk_value(0, unit_idx, true); 1422 1459 else 1423 1460 { 1424 - if (units_in[UNIT_IDX_HR] != 0) 1425 - { 1426 - talk_value(units_in[UNIT_IDX_HR], UNIT_HOUR, true); 1427 - } 1428 - if (units_in[UNIT_IDX_MIN] != 0) 1429 - { 1430 - talk_value(units_in[UNIT_IDX_MIN], UNIT_MIN, true); 1431 - } 1432 - if (units_in[UNIT_IDX_SEC] != 0) 1433 - { 1434 - talk_value(units_in[UNIT_IDX_SEC], UNIT_SEC, true); 1435 - } 1436 - if (units_in[UNIT_IDX_MS] != 0) 1437 - { 1438 - talk_value(units_in[UNIT_IDX_MS], UNIT_MS, true); 1439 - } 1461 + talk_time_value(units_in[UNIT_IDX_HR], UNIT_HOUR, true); 1462 + talk_time_value(units_in[UNIT_IDX_MIN], UNIT_MIN, true); 1463 + talk_time_value(units_in[UNIT_IDX_SEC], UNIT_SEC, true); 1464 + talk_time_value(units_in[UNIT_IDX_MS], UNIT_MS, true); 1440 1465 } 1441 1466 1442 1467 return -1; ··· 1445 1470 /* spell a string */ 1446 1471 int talk_spell(const char* spell, bool enqueue) 1447 1472 { 1448 - char c; /* currently processed char */ 1449 - 1450 - if (talk_temp_disable_count > 0) 1451 - return -1; /* talking has been disabled */ 1452 - if (!check_audio_status()) 1453 - return -1; 1454 - 1455 - if (!enqueue) 1456 - talk_shutup(); /* cut off all the pending stuff */ 1457 - 1458 - while ((c = *spell++) != '\0') 1459 - { 1460 - /* if this grows into too many cases, I should use a table */ 1461 - if (c >= 'A' && c <= 'Z') 1462 - talk_id(VOICE_CHAR_A + c - 'A', true); 1463 - else if (c >= 'a' && c <= 'z') 1464 - talk_id(VOICE_CHAR_A + c - 'a', true); 1465 - else if (c >= '0' && c <= '9') 1466 - talk_id(VOICE_ZERO + c - '0', true); 1467 - else if (c == '-') 1468 - talk_id(VOICE_MINUS, true); 1469 - else if (c == '+') 1470 - talk_id(VOICE_PLUS, true); 1471 - else if (c == '.') 1472 - talk_id(VOICE_DOT, true); 1473 - else if (c == ' ') 1474 - talk_id(VOICE_PAUSE, true); 1475 - else if (c == '/') 1476 - talk_id(VOICE_CHAR_SLASH, true); 1477 - } 1478 - 1479 - return 0; 1473 + return _talk_spell(spell, NULL_TERMINATED, enqueue); 1480 1474 } 1481 1475 1482 1476 void talk_disable(bool disable) ··· 1499 1493 talk_id(setting->lang_id,false); 1500 1494 } 1501 1495 1502 - 1503 1496 void talk_date(const struct tm *tm, bool enqueue) 1504 1497 { 1505 1498 const char *format = str(LANG_VOICED_DATE_FORMAT); 1506 1499 const char *ptr; 1507 1500 1508 - if (!enqueue) 1509 - talk_shutup(); /* cut off all the pending stuff */ 1501 + do_enqueue(enqueue); /* cut off all the pending stuff */ 1510 1502 1511 1503 for (ptr = format ; *ptr ; ptr++) { 1512 1504 switch(*ptr) { ··· 1586 1578 int buf_handle; 1587 1579 struct queue_entry qe; 1588 1580 1589 - const char talkfile[] = 1590 - LANG_DIR "/InvalidVoice_" DEFAULT_VOICE_LANG ".talk"; 1581 + const char talkfile[] = LANG_DIR "/InvalidVoice_" DEFAULT_VOICE_LANG ".talk"; 1591 1582 1592 1583 if (global_settings.talk_menu && talk_status != TALK_STATUS_OK) 1593 1584 {
+7
apps/talk.h
··· 64 64 TALK_STATUS_ERR_INCOMPATIBLE 65 65 }; 66 66 67 + enum talk_speakmode { 68 + /* voice mode: 0=off, 1=number, 2=spell */ 69 + TALK_SPEAK_OFF = 0, 70 + TALK_SPEAK_NUMBER, 71 + TALK_SPEAK_SPELL 72 + }; 73 + 67 74 #define UNIT_SHIFT (32-5) /* this many bits left from UNIT_xx enum */ 68 75 69 76 #define DECIMAL_SHIFT (32 - 8)