A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 1767 lines 53 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * 9 * Copyright (C) 2004 Jörg Hohensohn 10 * 11 * This module collects the Talkbox and voice UI functions. 12 * (Talkbox reads directory names from mp3 clips called thumbnails, 13 * the voice UI lets menus and screens "talk" from a voicefile in memory. 14 * 15 * This program is free software; you can redistribute it and/or 16 * modify it under the terms of the GNU General Public License 17 * as published by the Free Software Foundation; either version 2 18 * of the License, or (at your option) any later version. 19 * 20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21 * KIND, either express or implied. 22 * 23 ****************************************************************************/ 24 25#include <stdio.h> 26#include <stddef.h> 27#include "string-extra.h" 28#include "file.h" 29#include "system.h" 30#include "kernel.h" 31#include "settings.h" 32#include "settings_list.h" 33#include "splash.h" 34#include "voice_thread.h" 35#include "audio.h" 36#include "lang.h" 37#include "language.h" 38#include "talk.h" 39#include "metadata.h" 40/*#define LOGF_ENABLE*/ 41#include "logf.h" 42#include "bitswap.h" 43#include "plugin.h" /* plugin_get_buffer() */ 44#include "debug.h" 45#include "panic.h" 46#include "misc.h" /* time_split_units() */ 47#include "mv.h" 48 49/***************** Constants *****************/ 50 51#if MEMORYSIZE <= 2 52#define QUEUE_SIZE 64 53#else 54#define QUEUE_SIZE 128 /* must be a power of two */ 55#endif 56 57#define QUEUE_MASK (QUEUE_SIZE-1) 58const char* const dir_thumbnail_name = "_dirname.talk"; 59const char* const file_thumbnail_ext = ".talk"; 60 61/***************** Functional Macros *****************/ 62 63#define QUEUE_LEVEL ((queue_write - queue_read) & QUEUE_MASK) 64 65#define LOADED_MASK 0x80000000 /* MSB */ 66 67#ifndef DEFAULT_VOICE_LANG 68#define DEFAULT_VOICE_LANG "english" 69#endif 70 71/***************** Data types *****************/ 72 73struct clip_entry /* one entry of the index table */ 74{ 75 int offset; /* offset from start of voicefile file */ 76 int size; /* size of the clip */ 77}; 78 79struct voicefile_header /* file format of our voice file */ 80{ 81 int version; /* version of the voicefile */ 82 int target_id; /* the rockbox target the file was made for */ 83 int table; /* offset to index table, (=header size) */ 84 int id1_max; /* number of "normal" clips contained in above index */ 85 int id2_max; /* number of "voice only" clips contained in above index */ 86 /* The header is folled by the index tables (n*struct clip_entry), 87 * which is followed by the mp3/speex encoded clip data */ 88}; 89 90/***************** Globals *****************/ 91 92#if MEMORYSIZE <= 8 93/* On low memory swcodec targets the entire voice file wouldn't fit in memory 94 * together with codecs, so we load clips each time they are accessed. */ 95#define TALK_PROGRESSIVE_LOAD 96/* 70+ clips should fit into 100k */ 97#define MAX_CLIP_BUFFER_SIZE (100000) 98#endif 99 100#ifndef MAX_CLIP_BUFFER_SIZE 101/* 1GB should be enough for everybody; will be capped to voicefile size */ 102#define MAX_CLIP_BUFFER_SIZE (1<<30) 103#endif 104#define THUMBNAIL_RESERVE (50000) 105#define NULL_TERMINATED ((size_t)-1) 106/* Multiple thumbnails can be loaded back-to-back in this buffer. */ 107static volatile int thumbnail_buf_used SHAREDBSS_ATTR; /* length of data in 108 thumbnail buffer */ 109static struct voicefile_header voicefile; /* loaded voicefile */ 110static bool has_voicefile; /* a voicefile file is present */ 111static bool need_shutup; /* is there possibly any voice playing to be shutup */ 112static bool force_enqueue_next; /* enqueue next utterance even if enqueue is false */ 113static int queue_write; /* write index of queue, by application */ 114static int queue_read; /* read index of queue, by ISR context */ 115static enum talk_status talk_status = TALK_STATUS_OK; 116/* protects queue_read, queue_write and thumbnail_buf_used */ 117static struct mutex queue_mutex SHAREDBSS_ATTR; 118#define talk_queue_lock() ({ mutex_lock(&queue_mutex); }) 119#define talk_queue_unlock() ({ mutex_unlock(&queue_mutex); }) 120static int sent; /* how many bytes handed over to playback, owned by ISR */ 121static unsigned char curr_hd[3]; /* current frame header, for re-sync */ 122static unsigned char last_lang[MAX_FILENAME+1]; /* name of last used lang file (in talk_init) */ 123static bool talk_initialized; /* true if talk_init has been called */ 124static bool give_buffer_away; /* true if we should give the buffers away in shrink_callback if requested */ 125static int talk_temp_disable_count; /* if positive, temporarily disable voice UI (not saved) */ 126 127 /* size of the voice data in the voice file and the actually allocated buffer 128 * for it. voicebuf_size is always smaller or equal to voicefile_size */ 129static unsigned long voicefile_size, voicebuf_size; 130 131struct queue_entry /* one entry of the internal queue */ 132{ 133 int handle; /* buflib handle to the clip data */ 134 int length; /* total length of the clip */ 135 int remaining; /* bytes that still need to be deoded */ 136}; 137 138static struct buflib_context clip_ctx; 139 140struct clip_cache_metadata { 141 long tick; 142 int handle, voice_id; 143}; 144 145static int metadata_table_handle; 146static unsigned max_clips; 147static int cache_hits, cache_misses; 148 149static struct queue_entry queue[QUEUE_SIZE]; /* queue of scheduled clips */ 150static struct queue_entry silence, *last_clip; 151 152/***************** Private implementation *****************/ 153 154static int index_handle, talk_handle; 155 156static int move_callback(int handle, void *current, void *new) 157{ 158 (void)current; 159 if (handle == talk_handle && !buflib_context_relocate(&clip_ctx, new)) 160 return BUFLIB_CB_CANNOT_MOVE; 161 return BUFLIB_CB_OK; 162} 163 164 165static struct mutex read_buffer_mutex; 166 167static inline bool check_audio_status(void) 168{ 169 return true; 170} 171 172/* ISR (voice_callback()) must not run during moving of the clip buffer, 173 * because the MAS may get out-of-sync */ 174static void sync_callback(int handle, bool sync_on) 175{ 176 (void) handle; 177 if (sync_on) 178 mutex_lock(&read_buffer_mutex); 179 else 180 mutex_unlock(&read_buffer_mutex); 181} 182 183static ssize_t read_to_handle_ex(int fd, struct buflib_context *ctx, int handle, 184 int handle_offset, size_t count) 185{ 186 unsigned char *buf; 187 ssize_t ret; 188 mutex_lock(&read_buffer_mutex); 189 190 if (!ctx) 191 buf = core_get_data(handle); 192 else 193 buf = buflib_get_data(ctx, handle); 194 195 buf += handle_offset; 196 ret = read(fd, buf, count); 197 198 mutex_unlock(&read_buffer_mutex); 199 200 return ret; 201} 202 203static ssize_t read_to_handle(int fd, int handle, int handle_offset, size_t count) 204{ 205 return read_to_handle_ex(fd, NULL, handle, handle_offset, count); 206} 207 208 209static int shrink_callback(int handle, unsigned hints, void *start, size_t old_size) 210{ 211 (void)start;(void)old_size; 212 int *h; 213#if (MAX_CLIP_BUFFER_SIZE < (MEMORYSIZE<<20) || (MEMORYSIZE > 2)) 214 /* on low-mem and when the voice buffer size is not limited (i.e. 215 * on 2MB HWCODEC) we effectively own the entire buffer because 216 * the voicefile takes up all RAM. This blocks other Rockbox parts 217 * from allocating, especially during bootup. Therefore always give 218 * up the buffer and reload when clips are played back. On high-mem 219 * or when the clip buffer is limited to a few 100K this provision is 220 * not necessary. */ 221 if (give_buffer_away 222 && (hints & BUFLIB_SHRINK_POS_MASK) == BUFLIB_SHRINK_POS_MASK) 223#else 224 (void)hints; 225#endif 226 { 227 if (handle == talk_handle) 228 h = &talk_handle; 229 else if (handle == index_handle) 230 h = &index_handle; 231 else h = NULL; 232 233 mutex_lock(&read_buffer_mutex); 234 /* the clip buffer isn't usable without index table */ 235 if (handle == index_handle) 236 talk_handle = core_free(talk_handle); 237 if (h) 238 *h = core_free(handle); 239 mutex_unlock(&read_buffer_mutex); 240 241 return BUFLIB_CB_OK; 242 } 243 return BUFLIB_CB_CANNOT_SHRINK; 244} 245 246static struct buflib_callbacks talk_ops = { 247 .move_callback = move_callback, 248 .sync_callback = sync_callback, 249 .shrink_callback = shrink_callback, 250}; 251 252 253static int open_voicefile(void) 254{ 255 char fname[MAX_PATH]; 256 char* p_lang = DEFAULT_VOICE_LANG; /* default */ 257 258 if ( global_settings.lang_file[0] && 259 global_settings.lang_file[0] != 0xff ) 260 { /* try to open the voice file of the selected language */ 261 p_lang = (char *)global_settings.lang_file; 262 } 263 264 return open_pathfmt(fname, sizeof(fname), 265 O_RDONLY, LANG_DIR "/%s.voice", p_lang); 266} 267 268 269static int id2index(int id) 270{ 271 int index = id; 272 if (id > VOICEONLY_DELIMITER) 273 { /* voice-only entries use the second part of the table. 274 The first string comes after VOICEONLY_DELIMITER so we need to 275 substract VOICEONLY_DELIMITER + 1 */ 276 index -= VOICEONLY_DELIMITER + 1; 277 if (index >= voicefile.id2_max) 278 return -1; /* must be newer than we have */ 279 index += voicefile.id1_max; /* table 2 is behind table 1 */ 280 } 281 else 282 { /* normal use of the first table */ 283 if (id >= voicefile.id1_max) 284 return -1; /* must be newer than we have */ 285 } 286 return index; 287} 288 289#ifndef TALK_PROGRESSIVE_LOAD 290static int index2id(int index) 291{ 292 int id = index; 293 294 if (index >= voicefile.id2_max + voicefile.id1_max) 295 return -1; 296 297 if (index >= voicefile.id1_max) 298 { /* must be voice-only if it exceeds table 1 */ 299 id -= voicefile.id1_max; 300 /* The first string comes after VOICEONLY_DELIMITER so we need to 301 add VOICEONLY_DELIMITER + 1 */ 302 id += VOICEONLY_DELIMITER + 1; 303 } 304 305 return id; 306} 307#endif 308 309static int free_oldest_clip(void) 310{ 311 unsigned i; 312 int oldest = 0; 313 bool thumb = false; 314 long age, now, next_age; 315 struct clip_entry* clipbuf; 316 struct clip_cache_metadata *cc = buflib_get_data(&clip_ctx, metadata_table_handle); 317 for(age = i = 0, now = current_tick; i < max_clips; i++) 318 { 319 if (cc[i].handle) 320 { 321 next_age = (now - cc[i].tick); 322 if (thumb && cc[i].voice_id == VOICEONLY_DELIMITER && next_age > age) 323 { 324 /* thumb clips are freed first */ 325 age = now - cc[i].tick; 326 oldest = i; 327 } 328 else if (!thumb) 329 { 330 if (cc[i].voice_id == VOICEONLY_DELIMITER) 331 { 332 age = next_age; 333 oldest = i; 334 thumb = true; 335 } 336 else if (next_age > age && cc[i].voice_id != VOICE_PAUSE) 337 { 338 /* find the last-used clip but never consider silence */ 339 age = next_age; 340 oldest = i; 341 } 342 } 343 } 344 } 345 /* free the last one if no oldest one could be determined */ 346 cc = &cc[oldest]; 347 cc->handle = buflib_free(&clip_ctx, cc->handle); 348 /* need to clear the LOADED bit too (not for thumb clips) */ 349 if (cc->voice_id != VOICEONLY_DELIMITER) 350 { 351 clipbuf = core_get_data(index_handle); 352 clipbuf[id2index(cc->voice_id)].size &= ~LOADED_MASK; 353 } 354 return oldest; 355} 356 357/* common code for load_initial_clips() and get_clip() */ 358static void add_cache_entry(int clip_handle, int table_index, int id) 359{ 360 unsigned i; 361 struct clip_cache_metadata *cc = buflib_get_data(&clip_ctx, metadata_table_handle); 362 363 if (table_index != -1) 364 { 365 /* explicit slot; use that */ 366 cc = &cc[table_index]; 367 if (cc->handle > 0) panicf("%s(): Slot already used", __func__); 368 } 369 else 370 { /* find an empty slot */ 371 for(i = 0; cc[i].handle && i < max_clips; i++) ; 372 if (i == max_clips) /* no free slot in the cache table? */ 373 i = free_oldest_clip(); 374 cc = &cc[i]; 375 } 376 cc->handle = clip_handle; 377 cc->tick = current_tick; 378 cc->voice_id = id; 379} 380 381static ssize_t read_clip_data(int fd, int index, int clip_handle) 382{ 383 struct clip_entry* clipbuf; 384 size_t clipsize; 385 ssize_t ret = -1; 386 387 if (fd < 0) 388 { 389 buflib_free(&clip_ctx, clip_handle); 390 return -1; /* open error */ 391 } 392 393 clipbuf = core_get_data(index_handle); 394 /* this must not be called with LOADED_MASK set in clipsize */ 395 clipsize = clipbuf[index].size; 396 if (lseek(fd, clipbuf[index].offset, SEEK_SET) >= 0) 397 ret = read_to_handle_ex(fd, &clip_ctx, clip_handle, 0, clipsize); 398 399 if (ret < 0 || clipsize != (size_t)ret) 400 { 401 buflib_free(&clip_ctx, clip_handle); 402 return -2; /* read error */ 403 } 404 405 clipbuf = core_get_data(index_handle); 406 clipbuf[index].size |= LOADED_MASK; /* mark as loaded */ 407 408 return ret; 409} 410 411static void load_initial_clips(int fd) 412{ 413#if defined(TALK_PROGRESSIVE_LOAD) 414 (void) fd; 415#else 416 unsigned index, i; 417 unsigned num_clips = voicefile.id1_max + voicefile.id2_max; 418 419 for(index = i = 0; index < num_clips && i < max_clips; index++) 420 { 421 int handle; 422 struct clip_entry* clipbuf = core_get_data(index_handle); 423 size_t clipsize = clipbuf[index].size; 424 ssize_t ret; 425 426 if (clipsize == 0) /* clip not included in voicefile */ 427 continue; 428 429 handle = buflib_alloc(&clip_ctx, clipsize); 430 if (handle < 0) 431 break; 432 433 ret = read_clip_data(fd, index, handle); 434 if (ret < 0) 435 break; 436 437 add_cache_entry(handle, i++, index2id(index)); 438 } 439#endif 440} 441 442/* fetch a clip from the voice file */ 443static int get_clip(long id, struct queue_entry *q) 444{ 445 int index; 446 int retval = -1; 447 struct clip_entry* clipbuf; 448 size_t clipsize; 449 450 index = id2index(id); 451 if (index == -1 || index_handle <= 0) 452 return -1; 453 454 clipbuf = core_get_data(index_handle); 455 clipsize = clipbuf[index].size; 456 if (clipsize == 0) /* clip not included in voicefile */ 457 return -1; 458 459 if (!(clipsize & LOADED_MASK)) 460 { /* clip needs loading */ 461 int fd, handle, oldest = -1; 462 ssize_t ret; 463 cache_misses++; 464 /* free clips from cache until this one succeeds to allocate */ 465 while ((handle = buflib_alloc(&clip_ctx, clipsize)) < 0) 466 oldest = free_oldest_clip(); 467 /* handle should now hold a valid alloc. Load from disk 468 * and insert into cache */ 469 fd = open_voicefile(); 470 ret = read_clip_data(fd, index, handle); 471 close(fd); 472 if (ret < 0) 473 return ret; 474 /* finally insert into metadata table */ 475 add_cache_entry(handle, oldest, id); 476 retval = handle; 477 } 478 else 479 { /* clip is in memory already; find where it was loaded */ 480 cache_hits++; 481 struct clip_cache_metadata *cc; 482 static int i; 483 cc = buflib_get_data(&clip_ctx, metadata_table_handle); 484 for (i = 0; cc[i].voice_id != id || !cc[i].handle; i++) ; 485 cc[i].tick = current_tick; /* reset age */ 486 clipsize &= ~LOADED_MASK; /* without the extra bit gives true size */ 487 retval = cc[i].handle; 488 } 489 490 q->handle = retval; 491 q->length = clipsize; 492 q->remaining = clipsize; 493 return 0; 494} 495 496static bool load_index_table(int fd, const struct voicefile_header *hdr) 497{ 498 ssize_t ret; 499 500 if (index_handle > 0) /* nothing to do? */ 501 return true; 502 503 ssize_t alloc_size = (hdr->id1_max + hdr->id2_max) * sizeof(struct clip_entry); 504 index_handle = core_alloc_ex(alloc_size, &talk_ops); 505 if (index_handle < 0) 506 return false; 507 508 ret = read_to_handle(fd, index_handle, 0, alloc_size); 509 if (ret != alloc_size) 510 { 511 index_handle = core_free(index_handle); 512 return false; 513 } 514 515#ifdef ROCKBOX_LITTLE_ENDIAN 516 struct clip_entry *buf, *end; 517 buf = core_get_data(index_handle); 518 end = buf + hdr->id1_max + hdr->id2_max; 519 for (; buf != end; buf++) 520 { 521 buf->offset = swap32(buf->offset); 522 buf->size = swap32(buf->size); 523 } 524#endif 525 526 return true; 527} 528 529static bool load_header(int fd, struct voicefile_header *hdr) 530{ 531 ssize_t got_size = read(fd, hdr, sizeof(*hdr)); 532 if (got_size != sizeof(*hdr)) 533 return false; 534 535#ifdef ROCKBOX_LITTLE_ENDIAN 536 hdr->version = swap32(hdr->version); 537 hdr->target_id = swap32(hdr->target_id); 538 hdr->table = swap32(hdr->table); 539 hdr->id1_max = swap32(hdr->id1_max); 540 hdr->id2_max = swap32(hdr->id2_max); 541#endif 542 return true; 543} 544 545static bool create_clip_buffer(size_t max_size) 546{ 547 /* just allocate, populate on an as-needed basis later */ 548 talk_handle = core_alloc_ex(max_size, &talk_ops); 549 if (talk_handle < 0) 550 goto alloc_err; 551 552 buflib_init(&clip_ctx, core_get_data(talk_handle), max_size); 553 554 return true; 555 556alloc_err: 557 talk_status = TALK_STATUS_ERR_ALLOC; 558 index_handle = core_free(index_handle); 559 return false; 560} 561 562static inline int load_voicefile_failure(int fd) 563{ 564 if (fd >= 0) 565 close(fd); 566 return -1; 567} 568 569/* load the voice file into the mp3 buffer */ 570static bool load_voicefile_index(int fd) 571{ 572 if (fd < 0) /* failed to open */ 573 { 574 talk_status = TALK_STATUS_ERR_NOFILE; 575 return false; 576 } 577 578 /* load the header first */ 579 if (!load_header(fd, &voicefile)) 580 { 581 talk_status = TALK_STATUS_ERR_INCOMPATIBLE; 582 return false; 583 } 584 585 /* format check */ 586 if (voicefile.table == sizeof(struct voicefile_header)) 587 { 588 if (voicefile.version == VOICE_VERSION && 589 voicefile.target_id == TARGET_ID && 590 voicefile.id1_max == TALK_FINAL_ID && 591 voicefile.id2_max == TALK_FINAL_ID_VOICEONLY - VOICEONLY_DELIMITER - 1) 592 { 593 if (load_index_table(fd, &voicefile)) 594 return true; 595 } 596 } 597 598 talk_status = TALK_STATUS_ERR_INCOMPATIBLE; 599 logf("Incompatible voice file"); 600 logf("version %d expected %d", voicefile.version, VOICE_VERSION); 601 logf("target_id %d expected %d", voicefile.target_id, TARGET_ID); 602 logf("id1_max %d expected %d", voicefile.id1_max, TALK_FINAL_ID); 603 logf("id2_max %d expected %d", 604 voicefile.id2_max, TALK_FINAL_ID_VOICEONLY - VOICEONLY_DELIMITER); 605 return false; 606} 607 608/* this function caps the voicefile buffer and allocates it. It can 609 * be called after talk_init(), e.g. when the voice was temporarily disabled. 610 * The buffer size has to be capped again each time because the available 611 * audio buffer changes over time */ 612static bool load_voicefile_data(int fd) 613{ 614 voicebuf_size = voicefile_size; 615 /* cap to the max. number of clips or the size of the available audio 616 * buffer which we grab. We leave some to the rest of the system. 617 * While that reduces our buffer size it improves the chance that 618 * other allocs succeed without disabling voice which would require 619 * reloading the voice from disk (as we do not shrink our buffer when 620 * other code attempts new allocs these would fail) */ 621 size_t metadata_alloc_size; 622 ssize_t cap = MIN(MAX_CLIP_BUFFER_SIZE, audio_buffer_available() - (64<<10)); 623 if (UNLIKELY(cap < 0)) 624 { 625 logf("Not enough memory for voice. Disabling...\n"); 626 talk_status = TALK_STATUS_ERR_OOM; 627 return false; 628 } 629 else if (voicebuf_size > (size_t)cap) 630 voicebuf_size = cap; 631 632 /* just allocate, populate on an as-needed basis later 633 * re-create the clip buffer to ensure clip_ctx is up-to-date */ 634 talk_handle = core_free(talk_handle); 635 if (!create_clip_buffer(voicebuf_size)) 636 return false; 637 638 /* the first alloc is the clip metadata table */ 639 metadata_alloc_size = max_clips * sizeof(struct clip_cache_metadata); 640 metadata_table_handle = buflib_alloc(&clip_ctx, metadata_alloc_size); 641 if (metadata_table_handle <= 0) 642 { 643 talk_status = TALK_STATUS_ERR_OOM; 644 return false; 645 } 646 memset(buflib_get_data(&clip_ctx, metadata_table_handle), 0, metadata_alloc_size); 647 648 load_initial_clips(fd); 649 /* make sure to have the silence clip, if available return value can 650 * be cached globally even for TALK_PROGRESSIVE_LOAD because the 651 * VOICE_PAUSE clip is specially handled */ 652 get_clip(VOICE_PAUSE, &silence); 653 654 return true; 655} 656 657/* Use a static buffer to avoid difficulties with buflib during 658 * buffer passing to the voice_thread (swcodec). Clips can be played 659 in chunks so the size is not that important */ 660static unsigned char commit_buffer[2<<10]; 661 662static void* commit_transfer(struct queue_entry *qe, size_t *size) 663{ 664 void *buf = NULL; /* shut up gcc */ 665 static unsigned char *bufpos = commit_buffer; 666 sent = qe->remaining; 667 sent = MIN((size_t)sent, sizeof(commit_buffer)); 668 buf = buflib_get_data(&clip_ctx, qe->handle); 669 /* adjust buffer position to what has been played already */ 670 buf += (qe->length - qe->remaining); 671 memcpy(bufpos, buf, sent); 672 *size = sent; 673 674 return commit_buffer; 675} 676 677static inline bool is_silence(struct queue_entry *qe) 678{ 679 if (silence.handle > 0) /* silence clip available? */ 680 return (qe->handle == silence.handle); 681 else 682 return false; 683} 684 685/* called in ISR context (on HWCODEC) if mp3 data got consumed */ 686static void mp3_callback(const void** start, size_t* size) 687{ 688 struct queue_entry *qe = &queue[queue_read]; 689 /* voice_thread.c hints us how many of the buffer we provided it actually 690 * consumed. Because buffers have to be frame-aligned for speex 691 * it might be less than what we presented */ 692 if (*size) 693 sent = *size; 694 qe->remaining -= sent; /* we completed this */ 695 696 if (qe->remaining > 0) /* current clip not finished? */ 697 { /* feed the next 64K-1 chunk */ 698 *start = commit_transfer(qe, size); 699 return; 700 } 701 702 talk_queue_lock(); 703 704 /* increment read position for the just played clip */ 705 queue_read = (queue_read + 1) & QUEUE_MASK; 706 707 if (QUEUE_LEVEL == 0) 708 { 709 if (!is_silence(last_clip)) 710 { /* add silence clip when queue runs empty playing a voice clip, 711 * only if the previous clip wasn't already silence */ 712 queue[queue_write] = silence; 713 queue_write = (queue_write + 1) & QUEUE_MASK; 714 } 715 else 716 { 717 *size = 0; /* end of data */ 718 } 719 } 720 721 if (QUEUE_LEVEL != 0) /* queue is not empty? */ 722 { /* start next clip */ 723 last_clip = &queue[queue_read]; 724 *start = commit_transfer(last_clip, size); 725 curr_hd[0] = commit_buffer[1]; 726 curr_hd[1] = commit_buffer[2]; 727 curr_hd[2] = commit_buffer[3]; 728 } 729 730 talk_queue_unlock(); 731} 732 733/***************** Private routines *****************/ 734 735/* return if a voice codec is required or not */ 736static bool talk_voice_required(void) 737{ 738 return (has_voicefile) /* Voice file is available */ 739 || (global_settings.talk_dir_clip) /* Thumbnail clips are required */ 740 || (global_settings.talk_file_clip); 741} 742 743static bool talk_is_disabled(void) 744{ 745 if (talk_temp_disable_count > 0 || (!check_audio_status())) 746 return true; 747 return false; 748} 749 750static void do_enqueue(bool enqueue) 751{ 752 if (!enqueue) 753 talk_shutup(); /* cut off all the pending stuff */ 754} 755 756/* spell a string */ 757static int _talk_spell(const char* spell, size_t len, bool enqueue) 758{ 759 ucschar_t c; /* currently processed char */ 760 int button = BUTTON_NONE; 761 762 if (talk_is_disabled()) 763 return -1; 764 765 do_enqueue(enqueue); /* cut off all the pending stuff */ 766 767 const char *last = spell; 768 size_t len0 = len - 1; 769 /* Tokenize into UTF8 codepoints */ 770 while ((spell = utf8decode(spell, &c)), c != '\0') 771 { 772 len0 -= (spell - last); 773 if (len0 >= len) /* ie we underflow and wrap */ 774 break; 775 last = spell; 776 777 /* NOTE: This is COMPLETLY BROKEN for NON-ENGLISH */ 778 779 /* if this grows into too many cases, I should use a table */ 780 if (c >= 'A' && c <= 'Z') 781 talk_id(VOICE_CHAR_A + c - 'A', true); 782 else if (c >= 'a' && c <= 'z') 783 talk_id(VOICE_CHAR_A + c - 'a', true); 784 else if (c >= '0' && c <= '9') 785 talk_id(VOICE_ZERO + c - '0', true); 786 else if (c == '-') 787 talk_id(VOICE_MINUS, true); 788 else if (c == '+') 789 talk_id(VOICE_PLUS, true); 790 else if (c == '.') 791 talk_id(VOICE_DOT, true); 792 else if (c == ' ') 793 talk_id(VOICE_PAUSE, true); 794 else if (c == PATH_SEPCH) 795 talk_id(VOICE_CHAR_SLASH, true); 796 797 if (QUEUE_LEVEL == QUEUE_SIZE - 1) 798 button = button_get(false); /* prevent UI unresponsiveness */ 799 800 if (button == BUTTON_NONE || IS_SYSEVENT(button)) 801 while (QUEUE_LEVEL == QUEUE_SIZE - 1) /* queue full - busy loop */ 802 yield(); 803 else 804 return 0; /* truncate spelled-out string */ 805 } 806 return 0; 807} 808 809static int talk_spell_basename(const char *path, 810 const long *prefix_ids, bool enqueue) 811{ 812 if(prefix_ids) 813 { 814 talk_idarray(prefix_ids, enqueue); 815 enqueue = true; 816 } 817 const char *basename; 818 size_t len = path_basename(path, &basename); 819 820 return _talk_spell(basename, len, enqueue); 821} 822 823/* Say year like "nineteen ninety nine" instead of "one thousand 9 824 hundred ninety nine". */ 825static int talk_year(long year, bool enqueue) 826{ 827 int rem; 828 if(year < 1100 || (year >=2000 && year < 2100)) 829 /* just say it as a regular number */ 830 return talk_number(year, enqueue); 831 /* Say century */ 832 talk_number(year/100, enqueue); 833 rem = year%100; 834 if(rem == 0) 835 /* as in 1900 */ 836 return talk_id(VOICE_HUNDRED, true); 837 if(rem <10) 838 /* as in 1905 */ 839 talk_id(VOICE_ZERO, true); 840 /* sub-century year */ 841 return talk_number(rem, true); 842} 843 844/***************** Public routines *****************/ 845 846/* stop the playback and the pending clips */ 847void talk_force_shutup(void) 848{ 849 /* Had nothing to do (was frame boundary or not our clip) */ 850 voice_play_stop(); 851 talk_queue_lock(); 852 queue_write = queue_read = 0; /* reset the queue */ 853 thumbnail_buf_used = 0; 854 talk_queue_unlock(); 855 need_shutup = false; 856} 857 858/* Shutup the voice, except if force_enqueue_next is set. */ 859void talk_shutup(void) 860{ 861 if (need_shutup && !force_enqueue_next) 862 talk_force_shutup(); 863} 864 865/* schedule a clip, at the end or discard the existing queue */ 866static void queue_clip(struct queue_entry *clip, bool enqueue) 867{ 868 struct queue_entry *qe; 869 int queue_level; 870 871 do_enqueue(enqueue); /* cut off all the pending stuff */ 872 873 /* Something is being enqueued, force_enqueue_next override is no 874 longer in effect. */ 875 force_enqueue_next = false; 876 877 if (!clip->length) 878 return; /* safety check */ 879 talk_queue_lock(); 880 queue_level = QUEUE_LEVEL; /* check old level */ 881 qe = &queue[queue_write]; 882 883 if (queue_level < QUEUE_SIZE - 1) /* space left? */ 884 { 885 queue[queue_write] = *clip; 886 queue_write = (queue_write + 1) & QUEUE_MASK; 887 } 888 talk_queue_unlock(); 889 890 if (queue_level == 0) 891 { /* queue was empty, we have to do the initial start */ 892 size_t size; 893 void *buf = commit_transfer(qe, &size); 894 last_clip = qe; 895 voice_play_data(buf, size, mp3_callback); 896 curr_hd[0] = commit_buffer[1]; 897 curr_hd[1] = commit_buffer[2]; 898 curr_hd[2] = commit_buffer[3]; 899 } 900 901 need_shutup = true; 902 903 return; 904} 905 906/***************** Public implementation *****************/ 907 908void talk_init(void) 909{ 910 int filehandle; 911 talk_temp_disable_count = 0; 912 if (talk_initialized && !strcasecmp(last_lang, global_settings.lang_file)) 913 { 914 /* not a new file, nothing to do */ 915 return; 916 } 917 918 if(!talk_initialized) 919 { 920 mutex_init(&queue_mutex); 921 mutex_init(&read_buffer_mutex); 922 } 923 924 talk_force_shutup(); /* In case we have something speaking! */ 925 926 talk_initialized = true; 927 strmemccpy((char *)last_lang, (char *)global_settings.lang_file, 928 MAX_FILENAME); 929 930 /* reset some states */ 931 queue_write = queue_read = 0; /* reset the queue */ 932 memset(&voicefile, 0, sizeof(voicefile)); 933 934 silence.handle = -1; /* pause clip not accessible */ 935 voicefile_size = has_voicefile = 0; 936 /* need to free these as their size depends on the voice file, and 937 * this function is called when the talk voice file changes */ 938 index_handle = core_free(index_handle); 939 talk_handle = core_free(talk_handle); 940 /* don't free thumb handle, it doesn't depend on the actual voice file 941 * and so we can re-use it if it's already allocated in any event */ 942 943 filehandle = open_voicefile(); 944 if (filehandle >= 0) 945 { 946 if (!load_voicefile_index(filehandle)) 947 { 948 if (global_settings.talk_menu) 949 splashf(HZ, ID2P(LANG_READ_FAILED), ".voice"); 950 goto out; 951 } 952 /* Now determine the maximum buffer size needed for the voicefile. 953 * The below pretends the entire voicefile would be loaded. The buffer 954 * size is eventually capped later on in load_voicefile_data() */ 955 int num_clips = voicefile.id1_max + voicefile.id2_max; 956 int non_empty = num_clips; 957 int total_size = 0, avg_size; 958 struct clip_entry *clips = core_get_data(index_handle); 959 /* check for the average clip size to estimate the maximum number of 960 * clips the buffer can hold */ 961 for (int i = 0; i<num_clips; i++) 962 { 963 if (clips[i].size) 964 total_size += ALIGN_UP(clips[i].size, sizeof(void *)); 965 else 966 non_empty -= 1; 967 } 968 avg_size = total_size / non_empty; 969 max_clips = MIN((int)(MAX_CLIP_BUFFER_SIZE/avg_size) + 1, non_empty); 970 /* account for possible thumb clips */ 971 total_size += THUMBNAIL_RESERVE; 972 max_clips += 16; 973 voicefile_size = total_size; 974 has_voicefile = true; 975 } 976 else if (talk_voice_required()) 977 { 978 /* create buffer just for thumb clips */ 979 max_clips = 16; 980 voicefile_size = THUMBNAIL_RESERVE; 981 } 982 /* additionally to the clip we need a table to record the age of the clips 983 * so that, when memory is tight, only the most recently used ones are kept */ 984 voicefile_size += sizeof(struct clip_cache_metadata) * max_clips; 985 /* compensate a bit for buflib alloc overhead. */ 986 voicefile_size += BUFLIB_ALLOC_OVERHEAD * max_clips + 64; 987 988 load_voicefile_data(filehandle); 989 990 /* Initialize the actual voice clip playback engine as well */ 991 if (talk_voice_required()) 992 voice_thread_init(); 993 994out: 995 if (filehandle >= 0) 996 close(filehandle); /* close again, this was just to detect presence */ 997} 998 999/* somebody else claims the mp3 buffer, e.g. for regular play/record */ 1000void talk_buffer_set_policy(int policy) 1001{ 1002#ifdef DEBUG 1003 switch(policy) 1004 { 1005 case TALK_BUFFER_DEFAULT: 1006 case TALK_BUFFER_HOLD: give_buffer_away = false; break; 1007 case TALK_BUFFER_LOOSE: give_buffer_away = true; break; 1008 default: DEBUGF("Ignoring unknown policy\n"); break; 1009 } 1010#else 1011 give_buffer_away = (policy == TALK_BUFFER_LOOSE); 1012#endif 1013} 1014 1015/* play a voice ID from voicefile */ 1016int talk_id(int32_t id, bool enqueue) 1017{ 1018 int32_t unit; 1019 int decimals; 1020 struct queue_entry clip; 1021 bool isloaded = true; 1022 1023 if (!has_voicefile) 1024 return 0; /* no voicefile loaded, not an error -> pretend success */ 1025 if (talk_is_disabled()) 1026 return -1; 1027 1028 if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */ 1029 { 1030 int fd = open_voicefile(); 1031 if (fd < 0 || !load_voicefile_index(fd)) 1032 return load_voicefile_failure(fd); 1033 isloaded = load_voicefile_data(fd); 1034 close(fd); 1035 } 1036 1037 if (id == -1 || !isloaded) /* -1 is an indication for silence */ 1038 return -1; 1039 1040 decimals = (((uint32_t)id) >> DECIMAL_SHIFT) & 0x7; 1041 1042 /* check if this is a special ID, with a value */ 1043 unit = ((uint32_t)id) >> UNIT_SHIFT; 1044 if (unit || decimals) 1045 { /* sign-extend the value */ 1046 id = (uint32_t)id << (32-DECIMAL_SHIFT); 1047 id >>= (32-DECIMAL_SHIFT); 1048 1049 talk_value_decimal(id, unit, decimals, enqueue); /* speak it */ 1050 return 0; /* and stop, end of special case */ 1051 } 1052 1053 if (get_clip(id, &clip) < 0) 1054 return -1; /* not present */ 1055 1056#ifdef LOGF_ENABLE 1057 if (id > VOICEONLY_DELIMITER) 1058 logf("\ntalk_id: Say voice clip 0x%x\n", id); 1059 else 1060 logf("\ntalk_id: Say '%s'\n", str(id)); 1061#endif 1062 1063 queue_clip(&clip, enqueue); 1064 1065 return 0; 1066} 1067 1068/* Speaks zero or more IDs (from an array). */ 1069int talk_idarray(const long *ids, bool enqueue) 1070{ 1071 int r; 1072 if(!ids) 1073 return 0; 1074 while(*ids != TALK_FINAL_ID) 1075 { 1076 if((r = talk_id(*ids++, enqueue)) <0) 1077 return r; 1078 enqueue = true; 1079 } 1080 return 0; 1081} 1082 1083/* Make sure the current utterance is not interrupted by the next one. */ 1084void talk_force_enqueue_next(void) 1085{ 1086 force_enqueue_next = true; 1087} 1088 1089/* play a thumbnail from file */ 1090/* Returns size of spoken thumbnail, so >0 means something is spoken, 1091 <=0 means something went wrong. */ 1092static int _talk_file(const char* filename, 1093 const long *prefix_ids, bool enqueue) 1094{ 1095 int fd; 1096 int size; 1097 int handle = -1; 1098 int oldest = -1; 1099 1100 /* reload needed? */ 1101 if (talk_is_disabled()) 1102 return -1; 1103 1104 if (talk_handle <= 0 || index_handle <= 0) 1105 { 1106 fd = open_voicefile(); 1107 if (fd < 0 || !load_voicefile_index(fd)) 1108 return load_voicefile_failure(fd); 1109 load_voicefile_data(fd); 1110 close(fd); 1111 } 1112 1113 do_enqueue(enqueue); /* shutup now to free the thumbnail buffer */ 1114 1115 fd = open(filename, O_RDONLY); 1116 if (fd < 0) /* failed to open */ 1117 { 1118 return 0; 1119 } 1120 size = filesize(fd); 1121 1122 if (size > 0) 1123 { 1124 /* free clips from cache until this one succeeds to allocate */ 1125 while ((handle = buflib_alloc(&clip_ctx, size)) < 0) 1126 oldest = free_oldest_clip(); 1127 1128 size = read_to_handle_ex(fd, &clip_ctx, handle, 0, size); 1129 } 1130 1131 close(fd); 1132 1133 /* ToDo: find audio, skip ID headers and trailers */ 1134 1135 if (size > 0) /* Don't play missing clips */ 1136 { 1137 struct queue_entry clip; 1138 clip.handle = handle; 1139 clip.length = clip.remaining = size; 1140 if(prefix_ids) 1141 /* prefix thumbnail by speaking these ids, but only now 1142 that we know there's actually a thumbnail to be 1143 spoken. */ 1144 talk_idarray(prefix_ids, true); 1145 /* finally insert into metadata table. thumb clips go under the 1146 * VOICEONLY_DELIMITER id so the cache can distinguish them from 1147 * normal clips */ 1148 add_cache_entry(handle, oldest, VOICEONLY_DELIMITER); 1149 queue_clip(&clip, true); 1150 } 1151 else 1152 buflib_free(&clip_ctx, handle); 1153 1154 return size; 1155} 1156 1157int talk_file(const char *root, const char *dir, const char *file, 1158 const char *ext, const long *prefix_ids, bool enqueue) 1159/* Play a thumbnail file */ 1160{ 1161 char buf[MAX_PATH]; 1162 const char *fmt = "%s%s%s%s%s"; 1163 /* Does root end with a slash? */ 1164 if(root && root[0] && root[strlen(root)-1] != PATH_SEPCH) 1165 fmt = "%s" PATH_SEPSTR "%s%s%s%s"; 1166 snprintf(buf, MAX_PATH, fmt, 1167 root ? root : "", 1168 dir ? dir : "", dir ? PATH_SEPSTR : "", 1169 file ? file : "", 1170 ext ? ext : ""); 1171 return _talk_file(buf, prefix_ids, enqueue); 1172} 1173 1174/* Play a file's .talk thumbnail, fallback to spelling the filename, or 1175 go straight to spelling depending on settings. */ 1176int talk_file_or_spell(const char *dirname, const char *filename, 1177 const long *prefix_ids, bool enqueue) 1178{ 1179 if (global_settings.talk_file_clip) 1180 { /* .talk clips enabled */ 1181 if(talk_file(dirname, NULL, filename, file_thumbnail_ext, 1182 prefix_ids, enqueue) >0) 1183 return 0; 1184 } 1185 if (global_settings.talk_file == TALK_SPEAK_SPELL) 1186 /* Either .talk clips are disabled, or as a fallback */ 1187 return talk_spell_basename(filename, prefix_ids, enqueue); 1188 return 0; 1189} 1190 1191#ifdef HAVE_MULTIVOLUME 1192int talk_volume_id(int volume) 1193{ 1194 if (volume == -1) 1195 return 0; 1196 1197 int drive = volume_drive(volume); 1198 // XXX voice "VOLUME" or something like that? 1199 1200 talk_id(drive? LANG_DISK_NAME_MMC : LANG_DISK_NAME_INTERNAL, true); 1201 talk_value(volume, UNIT_INT, true); 1202 return 1; 1203} 1204#endif 1205 1206/* Play a directory's .talk thumbnail, fallback to spelling the filename, or 1207 go straight to spelling depending on settings. */ 1208int talk_dir_or_spell(const char* dirname, 1209 const long *prefix_ids, bool enqueue) 1210{ 1211 if (global_settings.talk_dir_clip) 1212 { /* .talk clips enabled */ 1213 if (talk_file(dirname, NULL, dir_thumbnail_name, NULL, 1214 prefix_ids, enqueue) >0) 1215 return 0; 1216 } 1217 if (global_settings.talk_dir == TALK_SPEAK_SPELL) { 1218 /* Either .talk clips disabled or as a fallback */ 1219 return talk_spell_basename(dirname, prefix_ids, enqueue); 1220 } 1221 return 0; 1222} 1223 1224/* Speak thumbnail for each component of a full path, again falling 1225 back or going straight to spelling depending on settings. */ 1226int talk_fullpath(const char* path, bool enqueue) 1227{ 1228 do_enqueue(enqueue); /* cut off all the pending stuff */ 1229 1230 if(path[0] != PATH_SEPCH) 1231 /* path ought to start with /... */ 1232 return talk_spell(path, true); 1233 talk_id(VOICE_CHAR_SLASH, true); 1234 char buf[MAX_PATH]; 1235 strmemccpy(buf, path, MAX_PATH); 1236 char *start = buf+1; /* start of current component */ 1237 char *ptr = strchr(start, PATH_SEPCH); /* end of current component */ 1238 while(ptr) { /* There are more slashes ahead */ 1239 /* temporarily poke a NULL at end of component to truncate string */ 1240 *ptr = '\0'; 1241#ifdef HAVE_MULTIVOLUME 1242 if (start == buf+1) { 1243 int vol = path_get_volume_id(buf+1); 1244 if (!talk_volume_id(vol)) 1245 talk_dir_or_spell(buf, NULL, true); 1246 } else 1247#endif 1248 talk_dir_or_spell(buf, NULL, true); 1249 *ptr = PATH_SEPCH; /* restore string */ 1250 talk_id(VOICE_CHAR_SLASH, true); 1251 start = ptr+1; /* setup for next component */ 1252 ptr = strchr(start, PATH_SEPCH); 1253 } 1254 1255 /* no more slashes, figure out final component */ 1256 if (!*start) { 1257 return 1; 1258 } 1259 1260 DIR* dir = opendir(buf); 1261 if (dir) { 1262 closedir(dir); 1263 return talk_dir_or_spell(buf, NULL, true); 1264 } else { 1265 return talk_file_or_spell(NULL, buf, NULL, true); 1266 } 1267} 1268 1269/* say a numeric value, this word ordering works for english, 1270 but not necessarily for other languages (e.g. german) */ 1271int talk_number(long n, bool enqueue) 1272{ 1273 int level = 2; /* mille count */ 1274 long mil = 1000000000; /* highest possible "-illion" */ 1275 1276 if (talk_is_disabled()) 1277 return -1; 1278 1279 do_enqueue(enqueue); /* cut off all the pending stuff */ 1280 1281 if (n==0) 1282 { /* special case */ 1283 talk_id(VOICE_ZERO, true); 1284 return 0; 1285 } 1286 1287 if (n<0) 1288 { 1289 talk_id(VOICE_MINUS, true); 1290 n = -n; 1291 } 1292 1293 while (n) 1294 { 1295 if (mil < 1) 1296 { 1297 talk_id(VOICE_CHAR_E, true); 1298 return 0; 1299 } 1300 int segment = n / mil; /* extract in groups of 3 digits */ 1301 n -= segment * mil; /* remove the used digits from number */ 1302 mil /= 1000; /* digit place for next round */ 1303 1304 if (segment) 1305 { 1306 int hundreds = segment / 100; 1307 int ones = segment % 100; 1308 1309 if (hundreds) 1310 { 1311 talk_id(VOICE_ZERO + hundreds, true); 1312 talk_id(VOICE_HUNDRED, true); 1313 } 1314 1315 struct queue_entry tens_swap; 1316 if (get_clip(VOICE_NUMERIC_TENS_SWAP_SEPARATOR, &tens_swap) >= 0) 1317 { 1318 /* direct indexing */ 1319 if (ones <= 20) 1320 { 1321 talk_id(VOICE_ZERO + ones, true); 1322 } 1323 else if (ones) 1324 { 1325 int tmp = ones % 10; 1326 if (tmp) 1327 { 1328 talk_id(VOICE_ZERO + tmp, true); 1329 talk_id(VOICE_NUMERIC_TENS_SWAP_SEPARATOR, true); 1330 } 1331 } 1332 /* combination indexing */ 1333 if (ones > 20) 1334 { 1335 int tens = ones/10 + 18; 1336 talk_id(VOICE_ZERO + tens, true); 1337 } 1338 } 1339 else 1340 { 1341 /* combination indexing */ 1342 if (ones > 20) 1343 { 1344 int tens = ones/10 + 18; 1345 talk_id(VOICE_ZERO + tens, true); 1346 ones %= 10; 1347 } 1348 1349 /* direct indexing */ 1350 if (ones) 1351 talk_id(VOICE_ZERO + ones, true); 1352 } 1353 1354 /* add billion, million, thousand */ 1355 if (mil) 1356 talk_id(VOICE_THOUSAND + level, true); 1357 } 1358 level--; 1359 } 1360 1361 return 0; 1362} 1363 1364/* Say time duration/interval. Input is time in seconds, 1365 say hours,minutes,seconds. */ 1366static int talk_time_unit(long secs, bool enqueue) 1367{ 1368 return talk_time_intervals(secs, UNIT_SEC, enqueue); 1369} 1370 1371void talk_fractional(char *tbuf, int value, int unit) 1372{ 1373 int i; 1374 /* strip trailing zeros from the fraction */ 1375 for (i = strlen(tbuf) - 1; (i >= 0) && (tbuf[i] == '0'); i--) 1376 tbuf[i] = '\0'; 1377 1378 talk_number(value, true); 1379 if (tbuf[0] != 0) 1380 { 1381 talk_id(LANG_POINT, true); 1382 talk_spell(tbuf, true); 1383 } 1384 talk_id(unit, true); 1385} 1386 1387int talk_value(long n, int unit, bool enqueue) 1388{ 1389 return talk_value_decimal(n, unit, 0, enqueue); 1390} 1391 1392/* singular/plural aware saying of a value */ 1393int talk_value_decimal(long n, int unit, int decimals, bool enqueue) 1394{ 1395 int unit_id; 1396 static const int unit_voiced[] = 1397 { /* lookup table for the voice ID of the units */ 1398 [0 ... UNIT_LAST-1] = -1, /* regular ID, int, signed */ 1399 [UNIT_MS] 1400 = VOICE_MILLISECONDS, /* here come the "real" units */ 1401 [UNIT_SEC] 1402 = VOICE_SECONDS, 1403 [UNIT_MIN] 1404 = VOICE_MINUTES, 1405 [UNIT_HOUR] 1406 = VOICE_HOURS, 1407 [UNIT_KHZ] 1408 = VOICE_KHZ, 1409 [UNIT_DB] 1410 = VOICE_DB, 1411 [UNIT_PERCENT] 1412 = VOICE_PERCENT, 1413 [UNIT_MAH] 1414 = VOICE_MILLIAMPHOURS, 1415 [UNIT_PIXEL] 1416 = VOICE_PIXEL, 1417 [UNIT_PER_SEC] 1418 = VOICE_PER_SEC, 1419 [UNIT_HERTZ] 1420 = VOICE_HERTZ, 1421 [UNIT_MB] 1422 = LANG_MEBIBYTE, 1423 [UNIT_KBIT] 1424 = VOICE_KBIT_PER_SEC, 1425 [UNIT_PM_TICK] 1426 = VOICE_PM_UNITS_PER_TICK, 1427 }; 1428 1429 static const int pow10[] = { /* 10^0 - 10^7 */ 1430 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 1431 }; 1432 1433 char tbuf[8]; 1434 char fmt[] = "%0nd"; 1435 1436 if (talk_is_disabled()) 1437 return -1; 1438 1439 /* special pronounciation for year number */ 1440 if (unit == UNIT_DATEYEAR) 1441 return talk_year(n, enqueue); 1442 /* special case for time duration */ 1443 if (unit == UNIT_TIME) 1444 return talk_time_unit(n, enqueue); 1445 1446 if (unit < 0 || unit >= UNIT_LAST) 1447 unit_id = -1; 1448 else 1449 unit_id = unit_voiced[unit]; 1450 1451 if ((n==1 || n==-1) /* singular? */ 1452 && unit_id >= VOICE_SECONDS && unit_id <= VOICE_HOURS) 1453 { 1454 unit_id--; /* use the singular for those units which have */ 1455 } 1456 1457 /* special case with a "plus" before */ 1458 if (n > 0 && (unit == UNIT_SIGNED || unit == UNIT_DB)) 1459 { 1460 talk_id(VOICE_PLUS, enqueue); 1461 enqueue = true; 1462 } 1463 1464 if (decimals) 1465 { 1466 /* needed for the "-0.5" corner case */ 1467 if (n < 0) 1468 { 1469 talk_id(VOICE_MINUS, enqueue); 1470 n = -n; 1471 } 1472 1473 fmt[2] = '0' + decimals; 1474 1475 snprintf(tbuf, sizeof(tbuf), fmt, n % pow10[decimals]); 1476 talk_fractional(tbuf, n / pow10[decimals], unit_id); 1477 1478 return 0; 1479 } 1480 1481 if (lang_units_first()) 1482 talk_id(unit_id, true); /* say the unit, if any */ 1483 talk_number(n, enqueue); /* say the number */ 1484 if (!lang_units_first()) 1485 talk_id(unit_id, true); /* say the unit, if any */ 1486 1487 return 0; 1488} 1489 1490static inline void talk_time_value(long n, int unit, bool enqueue) 1491{ 1492 if (n != 0) 1493 talk_value_decimal(n, unit, 0, enqueue); 1494} 1495 1496/* Say time duration/interval. Input is time unit specifies base unit, 1497 say hours,minutes,seconds, milliseconds. or any combination thereof */ 1498int talk_time_intervals(long time, int unit_idx, bool enqueue) 1499{ 1500 unsigned long units_in[UNIT_IDX_TIME_COUNT]; 1501 1502 if (talk_is_disabled()) 1503 return -1; 1504 1505 if (talk_handle <= 0 || index_handle <= 0) /* reload needed? */ 1506 { 1507 int fd = open_voicefile(); 1508 if (fd < 0 || !load_voicefile_index(fd)) 1509 return load_voicefile_failure(fd); 1510 load_voicefile_data(fd); 1511 close(fd); 1512 } 1513 1514 do_enqueue(enqueue); /* cut off all the pending stuff */ 1515 1516 time_split_units(unit_idx, labs(time), &units_in); 1517 1518 if (time < 0) 1519 talk_id(VOICE_MINUS, true); 1520 1521 if (time == 0) 1522 talk_value(0, unit_idx, true); 1523 else 1524 { 1525 talk_time_value(units_in[UNIT_IDX_HR], UNIT_HOUR, true); 1526 talk_time_value(units_in[UNIT_IDX_MIN], UNIT_MIN, true); 1527 talk_time_value(units_in[UNIT_IDX_SEC], UNIT_SEC, true); 1528 talk_time_value(units_in[UNIT_IDX_MS], UNIT_MS, true); 1529 } 1530 1531 return -1; 1532} 1533 1534/* spell a string */ 1535int talk_spell(const char* spell, bool enqueue) 1536{ 1537 return _talk_spell(spell, NULL_TERMINATED, enqueue); 1538} 1539 1540void talk_disable(bool disable) 1541{ 1542 if (disable) 1543 talk_temp_disable_count++; 1544 else 1545 talk_temp_disable_count--; 1546} 1547 1548void talk_setting(const void *global_settings_variable) 1549{ 1550 const struct settings_list *setting; 1551 if (!global_settings.talk_menu) 1552 return; 1553 setting = find_setting(global_settings_variable); 1554 if (setting == NULL) 1555 return; 1556 if (setting->lang_id) 1557 talk_id(setting->lang_id,false); 1558} 1559 1560void talk_date(const struct tm *tm, bool enqueue) 1561{ 1562 const char *format = str(LANG_VOICED_DATE_FORMAT); 1563 const char *ptr; 1564 1565 do_enqueue(enqueue); /* cut off all the pending stuff */ 1566 1567 for (ptr = format ; *ptr ; ptr++) { 1568 switch(*ptr) { 1569 case 'Y': 1570 talk_number(1900 + tm->tm_year, true); 1571 break; 1572 case 'A': 1573 talk_id(LANG_MONTH_JANUARY + tm->tm_mon, true); 1574 break; 1575 case 'm': 1576 talk_number(tm->tm_mon + 1, true); 1577 break; 1578 case 'd': 1579 talk_number(tm->tm_mday, true); 1580 break; 1581 default: 1582 break; 1583 } 1584 } 1585} 1586 1587void talk_time(const struct tm *tm, bool enqueue) 1588{ 1589 if (global_settings.timeformat == 1) 1590 { 1591 /* Voice the hour */ 1592 long am_pm_id = VOICE_AM; 1593 int hour = tm->tm_hour; 1594 if (hour >= 12) 1595 { 1596 am_pm_id = VOICE_PM; 1597 hour -= 12; 1598 } 1599 if (hour == 0) 1600 hour = 12; 1601 talk_number(hour, enqueue); 1602 1603 /* Voice the minutes */ 1604 if (tm->tm_min == 0) 1605 { 1606 /* Say o'clock if the minute is 0. */ 1607 talk_id(VOICE_OCLOCK, true); 1608 } 1609 else 1610 { 1611 /* Pronounce the leading 0 */ 1612 if(tm->tm_min < 10) 1613 talk_id(VOICE_OH, true); 1614 talk_number(tm->tm_min, true); 1615 } 1616 talk_id(am_pm_id, true); 1617 } 1618 else 1619 { 1620 /* Voice the time in 24 hour format */ 1621 if(tm->tm_hour < 10) 1622 talk_id(VOICE_OH, true); 1623 talk_number(tm->tm_hour, enqueue); 1624 if (tm->tm_min == 0) 1625 { 1626 talk_ids(true, VOICE_HUNDRED, VOICE_HOURS); 1627 } 1628 else 1629 { 1630 /* Pronounce the leading 0 */ 1631 if(tm->tm_min < 10) 1632 talk_id(VOICE_OH, true); 1633 talk_number(tm->tm_min, true); 1634 } 1635 } 1636} 1637 1638void talk_announce_voice_invalid(void) 1639{ 1640 int voice_fd; 1641 int voice_sz; 1642 int buf_handle; 1643 struct queue_entry qe; 1644 1645 char talkfile[MAX_PATH]; 1646 const char* const p_lang_def = DEFAULT_VOICE_LANG; /* default */ 1647 const char* p_lang = p_lang_def; 1648 1649 /* attempt to load Invalid voice clip in proper lang otherwise use default */ 1650 if (global_settings.lang_file[0] && global_settings.lang_file[0] != 0xff) 1651 p_lang = global_settings.lang_file; 1652 1653 while(1) 1654 { 1655 snprintf(talkfile, sizeof(talkfile), LANG_DIR "/InvalidVoice_%s.talk", p_lang); 1656 if (file_exists(talkfile) || p_lang == p_lang_def) 1657 break; /* if it exists or default talk file doesn't exist give up */ 1658 p_lang = p_lang_def; 1659 } 1660 1661 if (global_settings.talk_menu && talk_status != TALK_STATUS_OK) 1662 { 1663 voice_fd = open(talkfile, O_RDONLY); 1664 if (voice_fd < 0) 1665 goto out; /* can't open */ 1666 1667 voice_sz= lseek(voice_fd, 0, SEEK_END); 1668 if (voice_sz == 0 || voice_sz > (64<<10)) 1669 goto out; /* nothing here or too big */ 1670 1671 lseek(voice_fd, 0, SEEK_SET); 1672 /* add a bit extra for buflib overhead (2K) */ 1673 if (!create_clip_buffer(ALIGN_UP(voice_sz, sizeof(long)) + (2<<10))) 1674 goto out; 1675 mutex_lock(&read_buffer_mutex); 1676 buf_handle = buflib_alloc(&clip_ctx, ALIGN_UP(voice_sz, sizeof(long))); 1677 1678 if (buf_handle < 0) 1679 goto out; 1680 1681 if (read_to_handle_ex(voice_fd, &clip_ctx, buf_handle, 0, voice_sz) > 0) 1682 { 1683 voice_thread_init(); 1684 qe.handle = buf_handle; 1685 qe.length = qe.remaining = voice_sz; 1686 queue_clip(&qe, false); 1687 voice_wait(); 1688 } 1689 1690 mutex_unlock(&read_buffer_mutex); 1691 1692 buf_handle = buflib_free(&clip_ctx, buf_handle); 1693 1694 out: 1695 close(voice_fd); 1696 return; 1697 } 1698} 1699 1700bool talk_get_debug_data(struct talk_debug_data *data) 1701{ 1702 char* p_lang = DEFAULT_VOICE_LANG; /* default */ 1703 struct clip_cache_metadata *cc; 1704 1705 memset(data, 0, sizeof(*data)); 1706 1707 data->status = talk_status; 1708 1709 if (global_settings.lang_file[0] && global_settings.lang_file[0] != 0xff) 1710 p_lang = (char *)global_settings.lang_file; 1711 1712 strmemccpy(data->voicefile, p_lang, sizeof(data->voicefile)); 1713 1714 if (!has_voicefile || index_handle <= 0) 1715 { 1716 if (data->status == TALK_STATUS_OK) 1717 data->status = TALK_STATUS_ERR_NOFILE; 1718 1719 return false; 1720 } 1721 1722 struct clip_entry *clips = core_get_data(index_handle); 1723 int cached = 0; 1724 int real_clips = 0; 1725 1726 data->num_clips = voicefile.id1_max + voicefile.id2_max; 1727 data->avg_clipsize = data->max_clipsize = 0; 1728 data->min_clipsize = INT_MAX; 1729 for(int i = 0; i < data->num_clips; i++) 1730 { 1731 int size = clips[i].size & (~LOADED_MASK); 1732 if (!size) continue; 1733 real_clips += 1; 1734 if (size < data->min_clipsize) 1735 data->min_clipsize = size; 1736 if (size > data->max_clipsize) 1737 data->max_clipsize = size; 1738 data->avg_clipsize += size; 1739 } 1740 if (!(real_clips > 0)) 1741 { 1742 if (data->status == TALK_STATUS_OK) 1743 data->status = TALK_STATUS_ERR_NOFILE; 1744 1745 return false; 1746 } 1747 1748 cc = buflib_get_data(&clip_ctx, metadata_table_handle); 1749 for (int i = 0; i < (int) max_clips; i++) 1750 { 1751 if (cc[i].handle > 0) 1752 cached += 1; 1753 } 1754 data->avg_clipsize /= real_clips; 1755 data->num_empty_clips = data->num_clips - real_clips; 1756 data->memory_allocated = sizeof(commit_buffer) + sizeof(voicefile) 1757 + data->num_clips * sizeof(struct clip_entry) 1758 + voicebuf_size; 1759 data->memory_used = 0; 1760 if (talk_handle > 0) 1761 data->memory_used = data->memory_allocated - buflib_available(&clip_ctx); 1762 data->cached_clips = cached; 1763 data->cache_hits = cache_hits; 1764 data->cache_misses = cache_misses; 1765 1766 return true; 1767}