summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2020-07-15 12:00:09 -0400
committerWilliam Wilgus <wilgus.william@gmail.com>2020-07-27 20:47:03 -0400
commitbe04c4be0afbdef36f2968dc291af54360de8e23 (patch)
tree8b4e87d48b38bf98f4f7eb8bcbd62cc196cbbaff
parent3f828e9244140e7860e961917fc96e42b2a866ca (diff)
downloadrockbox-be04c4be0afbdef36f2968dc291af54360de8e23.tar.gz
rockbox-be04c4be0afbdef36f2968dc291af54360de8e23.zip
Voice TSR Plugin Demo
allows user to run plugin in background that voices status messages grouping is now working it counts ; as the end of a group sleep timer remaining is not voiced if sleep timer is not active TODO manual entries Change-Id: I39e8500df6440c07d2a3347513c749d5e155d1cc
-rw-r--r--apps/lang/english.lang206
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES5
-rw-r--r--apps/plugins/announce_status.c842
-rw-r--r--tools/voicefont.c2
5 files changed, 1055 insertions, 1 deletions
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index a424fd1e7f..8079884426 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -15904,3 +15904,209 @@
15904 xduoox20,xduoox3,xduoox3ii: "Double tap HOME to cancel." 15904 xduoox20,xduoox3,xduoox3ii: "Double tap HOME to cancel."
15905 </voice> 15905 </voice>
15906</phrase> 15906</phrase>
15907<phrase>
15908 id: LANG_DATE
15909 desc: for constructing time and date announcements
15910 user: core
15911 <source>
15912 *: "Date"
15913 </source>
15914 <dest>
15915 *: "Date"
15916 </dest>
15917 <voice>
15918 *: "Date"
15919 </voice>
15920</phrase>
15921<phrase>
15922 id: LANG_CLEAR_ALL
15923 desc:
15924 user: core
15925 <source>
15926 *: "Clear all"
15927 </source>
15928 <dest>
15929 *: "Clear all"
15930 </dest>
15931 <voice>
15932 *: "Clear all"
15933 </voice>
15934</phrase>
15935<phrase>
15936 id: LANG_CANCEL_0
15937 desc: CANCEL.
15938 user: core
15939 <source>
15940 *: "Cancel"
15941 </source>
15942 <dest>
15943 *: "Cancel"
15944 </dest>
15945 <voice>
15946 *: "Cancel"
15947 </voice>
15948</phrase>
15949<phrase>
15950 id: LANG_SAVE
15951 desc:
15952 user: core
15953 <source>
15954 *: "Save"
15955 </source>
15956 <dest>
15957 *: "Save"
15958 </dest>
15959 <voice>
15960 *: "Save"
15961 </voice>
15962</phrase>
15963<phrase>
15964 id: LANG_TIMEOUT
15965 desc:
15966 user: core
15967 <source>
15968 *: "Timeout"
15969 </source>
15970 <dest>
15971 *: "Timeout"
15972 </dest>
15973 <voice>
15974 *: "Timeout"
15975 </voice>
15976</phrase>
15977<phrase>
15978 id: LANG_TRACK
15979 desc: used in track x of y constructs
15980 user: core
15981 <source>
15982 *: none
15983 hotkey: "Track"
15984 </source>
15985 <dest>
15986 *: none
15987 hotkey: "Track"
15988 </dest>
15989 <voice>
15990 *: none
15991 hotkey: "Track"
15992 </voice>
15993</phrase>
15994<phrase>
15995 id: LANG_ELAPSED
15996 desc: prefix for elapsed playtime announcement
15997 user: core
15998 <source>
15999 *: none
16000 hotkey: "Elapsed"
16001 </source>
16002 <dest>
16003 *: none
16004 hotkey: "Elapsed"
16005 </dest>
16006 <voice>
16007 *: none
16008 hotkey: "Elapsed"
16009 </voice>
16010</phrase>
16011<phrase>
16012 id: LANG_ANNOUNCEMENT_FMT
16013 desc: format for wps hotkey announcement
16014 user: core
16015 <source>
16016 *: none
16017 hotkey: "Announcement format"
16018 </source>
16019 <dest>
16020 *: none
16021 hotkey: "Announcement format"
16022 </dest>
16023 <voice>
16024 *: none
16025 hotkey: "Announcement format"
16026 </voice>
16027</phrase>
16028<phrase>
16029 id: LANG_REMAIN
16030 desc: for constructs such as number of tracks remaining etc
16031 user: core
16032 <source>
16033 *: none
16034 hotkey: "Remain"
16035 </source>
16036 <dest>
16037 *: none
16038 hotkey: "Remain"
16039 </dest>
16040 <voice>
16041 *: none
16042 hotkey: "Remain"
16043 </voice>
16044</phrase>
16045<phrase>
16046 id: LANG_GROUPING
16047 desc:
16048 user: core
16049 <source>
16050 *: none
16051 hotkey: "Grouping"
16052 </source>
16053 <dest>
16054 *: none
16055 hotkey: "Grouping"
16056 </dest>
16057 <voice>
16058 *: none
16059 hotkey: "Grouping"
16060 </voice>
16061</phrase>
16062<phrase>
16063 id: LANG_ANNOUNCE_ON
16064 desc:
16065 user: core
16066 <source>
16067 *: none
16068 hotkey: "Announce on"
16069 </source>
16070 <dest>
16071 *: none
16072 hotkey: "Announce on"
16073 </dest>
16074 <voice>
16075 *: none
16076 hotkey: "Announce on"
16077 </voice>
16078</phrase>
16079<phrase>
16080 id: LANG_TRACK_CHANGE
16081 desc:
16082 user: core
16083 <source>
16084 *: none
16085 hotkey: "Track change"
16086 </source>
16087 <dest>
16088 *: none
16089 hotkey: "Track change"
16090 </dest>
16091 <voice>
16092 *: none
16093 hotkey: "Track change"
16094 </voice>
16095</phrase>
16096<phrase>
16097 id: LANG_HOLD_FOR_SETTINGS
16098 desc:
16099 user: core
16100 <source>
16101 *: none
16102 hotkey: "Hold for settings"
16103 </source>
16104 <dest>
16105 *: none
16106 hotkey: "Hold for settings"
16107 </dest>
16108 <voice>
16109 *: none
16110 hotkey: "Hold for settings"
16111 </voice>
16112</phrase>
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index bb8688347d..07bc4df847 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -1,6 +1,7 @@
12048,games 12048,games
2alpine_cdc,apps 2alpine_cdc,apps
3alarmclock,apps 3alarmclock,apps
4announce_status,demos
4autostart,apps 5autostart,apps
5battery_bench,apps 6battery_bench,apps
6bench_scaler,apps 7bench_scaler,apps
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 5d24bee8f6..0180c56faf 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -151,6 +151,11 @@ starfield.c
151vu_meter.c 151vu_meter.c
152wormlet.c 152wormlet.c
153 153
154#ifdef HAVE_HOTKEY
155announce_status.c
156#endif
157
158
154/* Plugins needing the grayscale lib on low-depth LCDs */ 159/* Plugins needing the grayscale lib on low-depth LCDs */
155fire.c 160fire.c
156plasma.c 161plasma.c
diff --git a/apps/plugins/announce_status.c b/apps/plugins/announce_status.c
new file mode 100644
index 0000000000..e46899f8cd
--- /dev/null
+++ b/apps/plugins/announce_status.c
@@ -0,0 +1,842 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 *
11 * Copyright (C) 2003-2005 Jörg Hohensohn
12 * Copyright (C) 2020 BILGUS
13 *
14 *
15 *
16 * Usage: Start plugin, it will stay in the background.
17 *
18 *
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation; either version 2
22 * of the License, or (at your option) any later version.
23 *
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
25 * KIND, either express or implied.
26 *
27 ****************************************************************************/
28
29#include "plugin.h"
30#include "lib/kbd_helper.h"
31#include "lib/configfile.h"
32
33/****************** constants ******************/
34#define MAX_GROUPS 7
35#define MAX_ANNOUNCE_WPS 63
36#define ANNOUNCEMENT_TIMEOUT 10
37#define GROUPING_CHAR ';'
38
39#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF)
40#define EV_OTHINSTANCE MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFE)
41#define EV_STARTUP MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x01)
42#define EV_TRACKCHANGE MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0x02)
43
44#define CFG_FILE "/VoiceTSR.cfg"
45#define CFG_VER 1
46
47#if CONFIG_RTC
48 #define K_TIME "DT D1;\n\n"
49 #define K_DATE "DD D2;\n\n"
50#else
51 #define K_TIME ""
52 #define K_DATE ""
53#endif
54
55#define K_TRACK_TA "TT TA;\n"
56#define K_TRACK "TE TL TR;\n"
57#define K_TRACK1 "T1 T2 T3;\n\n"
58#define K_PLAYLIST "PC PN PR P1 P2;\n"
59#define K_BATTERY "BP BM B1;\n"
60#define K_SLEEP "RS R2 R3;\n"
61#define K_RUNTIME "RT R1;"
62#define KEYBD_LAYOUT (K_TIME K_DATE K_TRACK_TA K_TRACK K_TRACK1 K_PLAYLIST K_BATTERY K_SLEEP K_RUNTIME)
63
64/****************** prototypes ******************/
65void print_scroll(char* string); /* implements a scrolling screen */
66
67int get_playtime(void); /* return the current track time in seconds */
68int get_tracklength(void); /* return the total length of the current track */
69int get_track(void); /* return the track number */
70void get_playmsg(void); /* update the play message with Rockbox info */
71
72void thread_create(void);
73void thread(void); /* the thread running it all */
74void thread_quit(void);
75static int voice_general_info(bool testing);
76static unsigned char* voice_info_group(unsigned char* current_token, bool testing);
77
78int main(const void* parameter); /* main loop */
79enum plugin_status plugin_start(const void* parameter); /* entry */
80
81
82/****************** data types ******************/
83
84/****************** globals ******************/
85/* communication to the worker thread */
86static struct
87{
88 bool exiting; /* signal to the thread that we want to exit */
89 unsigned int id; /* worker thread id */
90 struct event_queue queue; /* thread event queue */
91 long *stack;
92 ssize_t stacksize;
93 void *buf;
94 size_t buf_size;
95
96} gThread;
97
98static struct
99{
100 int interval;
101 int announce_on;
102 int grouping;
103
104 int timeout;
105 int count;
106 unsigned int index;
107 int bin_added;
108
109 unsigned char wps_fmt[MAX_ANNOUNCE_WPS+1];
110} gAnnounce;
111
112static struct configdata config[] =
113{
114 {TYPE_INT, 0, 10000, { .int_p = &gAnnounce.interval }, "Interval", NULL},
115 {TYPE_INT, 0, 2, { .int_p = &gAnnounce.announce_on }, "Announce", NULL},
116 {TYPE_INT, 0, 10, { .int_p = &gAnnounce.grouping }, "Grouping", NULL},
117 {TYPE_INT, 0, 10000, { .int_p = &gAnnounce.bin_added }, "Added", NULL},
118 {TYPE_STRING, 0, MAX_ANNOUNCE_WPS+1,
119 { .string = (char*)&gAnnounce.wps_fmt }, "Fmt", NULL},
120};
121
122const int gCfg_sz = sizeof(config)/sizeof(*config);
123/****************** communication with Rockbox playback ******************/
124
125#if 0
126/* return the track number */
127int get_track(void)
128{
129 //if (rb->audio_status() == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE))
130 struct mp3entry* p_mp3entry;
131
132 p_mp3entry = rb->audio_current_track();
133 if (p_mp3entry == NULL)
134 return 0;
135
136 return p_mp3entry->index + 1; /* track numbers start with 1 */
137}
138#endif
139
140static void playback_event_callback(unsigned short id, void *data)
141{
142 (void)id;
143 (void)data;
144 rb->queue_post(&gThread.queue, EV_TRACKCHANGE, 0);
145}
146
147/****************** config functions *****************/
148static void config_set_defaults(void)
149{
150 gAnnounce.bin_added = 0;
151 gAnnounce.interval = ANNOUNCEMENT_TIMEOUT;
152 gAnnounce.announce_on = 0;
153 gAnnounce.grouping = 0;
154 gAnnounce.wps_fmt[0] = '\0';
155}
156
157static void config_reset_voice(void)
158{
159 /* don't want to change these so save a copy */
160 int interval = gAnnounce.interval;
161 int announce = gAnnounce.announce_on;
162 int grouping = gAnnounce.grouping;
163
164 if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
165 {
166 rb->splash(100, "ERROR!");
167 return;
168 }
169
170 /* restore other settings */
171 gAnnounce.interval = interval;
172 gAnnounce.announce_on = announce;
173 gAnnounce.grouping = grouping;
174}
175
176/****************** helper fuctions ******************/
177
178void announce(void)
179{
180 rb->talk_force_shutup();
181 rb->sleep(HZ / 2);
182 voice_general_info(false);
183 //rb->talk_force_enqueue_next();
184}
185
186static void announce_test(void)
187{
188 rb->talk_force_shutup();
189 rb->sleep(HZ / 2);
190 voice_info_group(gAnnounce.wps_fmt, true);
191
192 //rb->talk_force_enqueue_next();
193}
194
195static void announce_add(const char *str)
196{
197 int len_cur = rb->strlen(gAnnounce.wps_fmt);
198 int len_str = rb->strlen(str);
199 if (len_cur + len_str > MAX_ANNOUNCE_WPS)
200 return;
201 rb->strcpy(&gAnnounce.wps_fmt[len_cur], str);
202 announce_test();
203
204}
205
206static int _playlist_get_display_index(struct playlist_info *playlist)
207{
208 /* equivalent of the function found in playlist.c */
209 if(!playlist)
210 return -1;
211 /* first_index should always be index 0 for display purposes */
212 int index = playlist->index;
213 index -= playlist->first_index;
214 if (index < 0)
215 index += playlist->amount;
216
217 return index + 1;
218}
219
220static enum themable_icons icon_callback(int selected_item, void * data)
221{
222 (void)data;
223
224 if(selected_item < MAX_GROUPS && selected_item >= 0)
225 {
226 int bin = 1 << (selected_item);
227 if ((gAnnounce.bin_added & bin) == bin)
228 return Icon_Submenu;
229 }
230
231 return Icon_NOICON;
232}
233
234static int announce_menu_cb(int action,
235 const struct menu_item_ex *this_item,
236 struct gui_synclist *this_list)
237{
238 (void)this_item;
239 unsigned short *kbd_p = gThread.buf;
240 size_t kbd_bufsz = gThread.buf_size;
241
242 int selection = rb->gui_synclist_get_sel_pos(this_list);
243
244 if(action == ACTION_ENTER_MENUITEM)
245 {
246 rb->gui_synclist_set_icon_callback(this_list, icon_callback);
247 }
248 else if ((action == ACTION_STD_OK))
249 {
250 //rb->splashf(100, "%d", selection);
251 if (selection < MAX_GROUPS && selection >= 0) /* only add premade tags once */
252 {
253 int bin = 1 << (selection);
254 if ((gAnnounce.bin_added & bin) == bin)
255 return 0;
256
257 gAnnounce.bin_added |= bin;
258 }
259
260 switch(selection) {
261
262 case 0: /*Time*/
263 announce_add("D1Dt ;");
264 break;
265 case 1: /*Date*/
266 announce_add("D2Dd ;");
267 break;
268 case 2: /*Track*/
269 announce_add("TT T1TeT2Tr ;");
270 break;
271 case 3: /*Playlist*/
272 announce_add("P1PC P2PN ;");
273 break;
274 case 4: /*Battery*/
275 announce_add("B1Bp ;");
276 break;
277 case 5: /*Sleep*/
278 announce_add("R2RsR3 ;");
279 break;
280 case 6: /*Runtime*/
281 announce_add("R1Rt ;");
282 break;
283 case 7: /* sep */
284 break;
285 case 8: /*Clear All*/
286 gAnnounce.wps_fmt[0] = '\0';
287 gAnnounce.bin_added = 0;
288 rb->splash(HZ / 2, ID2P(LANG_RESET_DONE_CLEAR));
289 break;
290 case 9: /* inspect it */
291 if (!kbd_create_layout(KEYBD_LAYOUT, kbd_p, kbd_bufsz))
292 kbd_p = NULL;
293
294 rb->kbd_input(gAnnounce.wps_fmt, MAX_ANNOUNCE_WPS, kbd_p);
295 break;
296 case 10: /*test it*/
297 announce_test();
298 break;
299 case 11: /*cancel*/
300 config_reset_voice();
301 return ACTION_STD_CANCEL;
302 case 12: /* save */
303 return ACTION_STD_CANCEL;
304 default:
305 return action;
306 }
307 rb->gui_synclist_draw(this_list); /* redraw */
308 return 0;
309 }
310
311 return action;
312}
313
314static int announce_menu(void)
315{
316 int selection = 0;
317
318 MENUITEM_STRINGLIST(announce_menu, "Announcements", announce_menu_cb,
319 ID2P(LANG_TIME),
320 ID2P(LANG_DATE),
321 ID2P(LANG_TRACK),
322 ID2P(LANG_PLAYLIST),
323 ID2P(LANG_BATTERY_MENU),
324 ID2P(LANG_SLEEP_TIMER),
325 ID2P(LANG_RUNNING_TIME),
326 ID2P(VOICE_BLANK),
327 ID2P(LANG_CLEAR_ALL),
328 ID2P(LANG_ANNOUNCEMENT_FMT),
329 ID2P(LANG_VOICE),
330 ID2P(LANG_CANCEL_0),
331 ID2P(LANG_SAVE));
332
333 selection = rb->do_menu(&announce_menu, &selection, NULL, true);
334 if (selection == MENU_ATTACHED_USB)
335 return PLUGIN_USB_CONNECTED;
336
337 return 0;
338}
339
340/**
341 Shows the settings menu
342 */
343static int settings_menu(void)
344{
345 int selection = 0;
346 //bool old_val;
347
348 MENUITEM_STRINGLIST(settings_menu, "Announce Settings", NULL,
349 ID2P(LANG_TIMEOUT),
350 ID2P(LANG_ANNOUNCE_ON),
351 ID2P(LANG_GROUPING),
352 ID2P(LANG_ANNOUNCEMENT_FMT),
353 ID2P(VOICE_BLANK),
354 ID2P(LANG_MENU_QUIT),
355 ID2P(LANG_SAVE_EXIT));
356
357 static const struct opt_items announce_options[] = {
358 { STR(LANG_OFF)},
359 { STR(LANG_TRACK_CHANGE)},
360 };
361
362 do {
363 selection=rb->do_menu(&settings_menu,&selection, NULL, true);
364 switch(selection) {
365
366 case 0:
367 rb->set_int(rb->str(LANG_TIMEOUT), "s", UNIT_SEC,
368 &gAnnounce.interval, NULL, 1, 1, 360, NULL );
369 break;
370 case 1:
371 rb->set_option(rb->str(LANG_ANNOUNCE_ON),
372 &gAnnounce.announce_on, INT, announce_options, 2, NULL);
373 break;
374 case 2:
375 rb->set_int(rb->str(LANG_GROUPING), "", 1,
376 &gAnnounce.grouping, NULL, 1, 0, 7, NULL );
377 break;
378 case 3:
379 announce_menu();
380 break;
381 case 4: /*sep*/
382 continue;
383 case 5:
384 return -1;
385 break;
386 case 6:
387 configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
388 return 0;
389 break;
390
391 case MENU_ATTACHED_USB:
392 return PLUGIN_USB_CONNECTED;
393 default:
394 return 0;
395 }
396 } while ( selection >= 0 );
397 return 0;
398}
399
400
401/****************** main thread + helper ******************/
402void thread(void)
403{
404 long interval;
405 long last_tick = *rb->current_tick; /* for 1 sec tick */
406
407 struct queue_event ev;
408 while (!gThread.exiting)
409 {
410 rb->queue_wait(&gThread.queue, &ev);
411 interval = gAnnounce.interval * HZ;
412 switch (ev.id)
413 {
414 case EV_EXIT:
415 return;
416 case EV_OTHINSTANCE:
417 if (*rb->current_tick - last_tick >= interval)
418 {
419 last_tick += interval;
420 rb->sleep(0);
421 announce();
422 }
423 break;
424 case EV_STARTUP:
425 rb->beep_play(1500, 100, 1000);
426 break;
427 case EV_TRACKCHANGE:
428 rb->sleep(0);
429 announce();
430 break;
431 }
432 }
433}
434
435void plugin_buffer_init(void)
436{
437 if (gThread.buf == 0)
438 {
439 rb->memset(&gThread, 0, sizeof(gThread));
440 gThread.buf = rb->plugin_get_buffer(&gThread.buf_size);
441 ALIGN_BUFFER(gThread.buf, gThread.buf_size, 4);
442 }
443}
444
445void thread_create(void)
446{
447 /* init the worker thread */
448 gThread.stacksize = gThread.buf_size;
449 gThread.buf_size -= gThread.stacksize;
450
451 gThread.stack = (long *) gThread.buf + gThread.buf_size;
452
453 ALIGN_BUFFER(gThread.stack, gThread.stacksize, 4);
454
455 if (gThread.stacksize < DEFAULT_STACK_SIZE)
456 {
457 rb->splash(HZ*2, "Out of memory");
458 gThread.exiting = true;
459 gThread.id = UINT_MAX;
460 return;
461 }
462
463
464 /* put the thread's queue in the bcast list */
465 rb->queue_init(&gThread.queue, true);
466
467 gThread.id = rb->create_thread(thread, gThread.stack, gThread.stacksize,
468 0, "vTSR"
469 IF_PRIO(, PRIORITY_BACKGROUND)
470 IF_COP(, CPU));
471 rb->queue_post(&gThread.queue, EV_STARTUP, 0);
472 rb->yield();
473
474}
475
476void thread_quit(void)
477{
478 if (!gThread.exiting) {
479 rb->queue_post(&gThread.queue, EV_EXIT, 0);
480 rb->thread_wait(gThread.id);
481 /* remove the thread's queue from the broadcast list */
482 rb->queue_delete(&gThread.queue);
483 gThread.exiting = true;
484 }
485}
486
487/* callback to end the TSR plugin, called before a new one gets loaded */
488static bool exit_tsr(bool reenter)
489{
490 if (reenter)
491 {
492 rb->queue_post(&gThread.queue, EV_OTHINSTANCE, 0);
493 return false; /* dont let it start again */
494 }
495 thread_quit();
496
497 return true;
498}
499
500
501/****************** main ******************/
502
503int plugin_main(const void* parameter)
504{
505 (void)parameter;
506 bool settings = false;
507 int i = 0;
508
509 gAnnounce.index = 0;
510 gAnnounce.timeout = 0;
511
512 rb->talk_id(LANG_HOLD_FOR_SETTINGS, false);
513 rb->splash(HZ / 2, "Announce Status");
514
515 if (configfile_load(CFG_FILE, config, gCfg_sz, CFG_VER) < 0)
516 {
517 /* If the loading failed, save a new config file */
518 config_set_defaults();
519 configfile_save(CFG_FILE, config, gCfg_sz, CFG_VER);
520
521 rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
522 }
523
524 rb->splash(HZ, ID2P(LANG_HOLD_FOR_SETTINGS));
525
526 rb->button_clear_queue();
527 if (rb->button_get_w_tmo(HZ) > BUTTON_NONE)
528 {
529 while ((rb->button_get(false) & BUTTON_REL) != BUTTON_REL)
530 {
531 if (i & 1)
532 rb->beep_play(800, 100, 1000);
533
534 if (++i > 15)
535 {
536 settings = true;
537 break;
538 }
539 sleep(HZ / 5);
540 }
541 }
542
543 plugin_buffer_init(); /* need buffer for custom keyboard layout */
544
545 if (settings)
546 {
547 rb->splash(100, ID2P(LANG_SETTINGS));
548 int ret = settings_menu();
549 if (ret < 0)
550 return 0;
551 }
552
553 gAnnounce.timeout = *rb->current_tick;
554 rb->plugin_tsr(exit_tsr); /* stay resident */
555
556 if (gAnnounce.announce_on == 1)
557 rb->add_event(PLAYBACK_EVENT_TRACK_CHANGE, playback_event_callback);
558
559 thread_create();
560#ifdef DEBUG
561 return rb->default_event_handler(button);
562#else
563 return 0;
564#endif
565}
566
567
568/***************** Plugin Entry Point *****************/
569
570
571enum plugin_status plugin_start(const void* parameter)
572{
573 /* now go ahead and have fun! */
574 int ret = plugin_main(parameter);
575
576 rb->remove_event(PLAYBACK_EVENT_START_PLAYBACK, playback_event_callback);
577 return (ret==0) ? PLUGIN_OK : PLUGIN_ERROR;
578}
579
580static int voice_general_info(bool testing)
581{
582 unsigned char* infotemplate = gAnnounce.wps_fmt;
583
584 if (gAnnounce.index >= rb->strlen(gAnnounce.wps_fmt))
585 gAnnounce.index = 0;
586
587 long current_tick = *rb->current_tick;
588
589 if (*infotemplate == 0)
590 {
591 #if CONFIG_RTC
592 /* announce the time */
593 voice_info_group("D1Dt ", false);
594 #else
595 /* announce elapsed play for this track */
596 voice_info_group("T1Te ", false);
597 #endif
598 return -1;
599 }
600
601 if (TIME_BEFORE(current_tick, gAnnounce.timeout))
602 {
603 return -2;
604 }
605
606 gAnnounce.timeout = current_tick + gAnnounce.interval * HZ;
607
608 rb->talk_shutup();
609
610 gAnnounce.count = 0;
611 infotemplate = voice_info_group(&infotemplate[gAnnounce.index], testing);
612 gAnnounce.index = (infotemplate - gAnnounce.wps_fmt) + 1;
613
614 return 0;
615}
616
617static unsigned char* voice_info_group(unsigned char* current_token, bool testing)
618{
619 unsigned char current_char;
620 bool skip_next_group = false;
621 gAnnounce.count = 0;
622
623 while (*current_token != 0)
624 {
625 //rb->splash(10, current_token);
626 current_char = toupper(*current_token);
627 if (current_char == 'D')
628 {
629 /*
630 Date and time functions
631 */
632 current_token++;
633
634 current_char = toupper(*current_token);
635
636#if CONFIG_RTC
637 struct tm *tm = rb->get_time();
638
639 if (true) //(valid_time(tm))
640 {
641 if (current_char == 'T')
642 {
643 rb->talk_time(tm, true);
644 }
645 else if (current_char == 'D')
646 {
647 rb->talk_date(tm, true);
648 }
649 /* prefix suffix connectives */
650 else if (current_char == '1')
651 {
652 rb->talk_id(LANG_TIME, true);
653 }
654 else if (current_char == '2')
655 {
656 rb->talk_id(LANG_DATE, true);
657 }
658 }
659#endif
660 }
661 else if (current_char == 'R')
662 {
663 /*
664 Sleep timer and runtime
665 */
666 int sleep_remaining = sleep_remaining = rb->get_sleep_timer();
667 int runtime;
668
669 current_token++;
670 current_char = toupper(*current_token);
671 if (current_char == 'T')
672 {
673 runtime = rb->global_status->runtime;
674 rb->talk_value(runtime, UNIT_TIME, true);
675 }
676 /* prefix suffix connectives */
677 else if (current_char == '1')
678 {
679 rb->talk_id(LANG_RUNNING_TIME, true);
680 }
681 else if (testing || sleep_remaining > 0)
682 {
683 if (current_char == 'S')
684 {
685 rb->talk_value(sleep_remaining, UNIT_TIME, true);
686 }
687 /* prefix suffix connectives */
688 else if (current_char == '2')
689 {
690 rb->talk_id(LANG_SLEEP_TIMER, true);
691 }
692 else if (current_char == '3')
693 {
694 rb->talk_id(LANG_REMAIN, true);
695 }
696 }
697 else if (sleep_remaining == 0)
698 {
699 skip_next_group = true;
700 }
701
702 }
703 else if (current_char == 'T')
704 {
705 /*
706 Current track information
707 */
708 current_token++;
709
710 current_char = toupper(*current_token);
711
712 struct mp3entry* id3 = rb->audio_current_track();
713
714 int elapsed_length = id3->elapsed / 1000;
715 int track_length = id3->length / 1000;
716 int track_remaining = track_length - elapsed_length;
717
718 if (current_char == 'E')
719 {
720 rb->talk_value(elapsed_length, UNIT_TIME, true);
721 }
722 else if (current_char == 'L')
723 {
724 rb->talk_value(track_length, UNIT_TIME, true);
725 }
726 else if (current_char == 'R')
727 {
728 rb->talk_value(track_remaining, UNIT_TIME, true);
729 }
730 else if (current_char == 'T' && id3->title)
731 {
732 rb->talk_spell(id3->title, true);
733 }
734 else if (current_char == 'A' && id3->albumartist)
735 {
736 rb->talk_spell(id3->albumartist, true);
737 }
738 /* prefix suffix connectives */
739 else if (current_char == '1')
740 {
741 rb->talk_id(LANG_ELAPSED, true);
742 }
743 else if (current_char == '2')
744 {
745 rb->talk_id(LANG_REMAIN, true);
746 }
747 else if (current_char == '3')
748 {
749 rb->talk_id(LANG_OF, true);
750 }
751 }
752 else if (current_char == 'P')
753 {
754 /*
755 Current playlist information
756 */
757 current_token++;
758
759 current_char = toupper(*current_token);
760 struct playlist_info *pl;
761 int current_track = 0;
762 int remaining_tracks = 0;
763 int total_tracks = rb->playlist_amount();
764
765 if (!isdigit(current_char)) {
766 pl = rb->playlist_get_current();
767 current_track = _playlist_get_display_index(pl);
768 remaining_tracks = total_tracks - current_track;
769 }
770
771 if (total_tracks > 0 || testing)
772 {
773 if (current_char == 'C')
774 {
775 rb->talk_number(current_track, true);
776 }
777 else if (current_char == 'N')
778 {
779 rb->talk_number(total_tracks, true);
780 }
781 else if (current_char == 'R')
782 {
783 rb->talk_number(remaining_tracks, true);
784 }
785 /* prefix suffix connectives */
786 else if (current_char == '1')
787 {
788 rb->talk_id(LANG_TRACK, true);
789 }
790 else if (current_char == '2')
791 {
792 rb->talk_id(LANG_OF, true);
793 }
794 }
795 else if (total_tracks == 0)
796 skip_next_group = true;
797 }
798 else if (current_char == 'B')
799 {
800 /*
801 Battery
802 */
803 current_token++;
804
805 current_char = toupper(*current_token);
806
807 if (current_char == 'P')
808 {
809 rb->talk_value(rb->battery_level(), UNIT_PERCENT, true);
810 }
811 else if (current_char == 'M')
812 {
813 rb->talk_value(rb->battery_time() * 60, UNIT_TIME, true);
814 }
815 /* prefix suffix connectives */
816 else if (current_char == '1')
817 {
818 rb->talk_id(LANG_BATTERY_TIME, true);
819 }
820 }
821 else if (current_char == ' ')
822 {
823 /*
824 Catch your breath
825 */
826 rb->talk_id(VOICE_PAUSE, true);
827 }
828 else if (current_char == GROUPING_CHAR && gAnnounce.grouping > 0)
829 {
830 gAnnounce.count++;
831
832 if (gAnnounce.count >= gAnnounce.grouping && !testing && !skip_next_group)
833 break;
834
835 skip_next_group = false;
836
837 }
838 current_token++;
839 }
840
841 return current_token;
842}
diff --git a/tools/voicefont.c b/tools/voicefont.c
index 9a8a7fbc02..b732f49c07 100644
--- a/tools/voicefont.c
+++ b/tools/voicefont.c
@@ -153,7 +153,7 @@ int voicefont(FILE* voicefontids,int targetnum,char* filedir, FILE* output, unsi
153 } 153 }
154 else /* second run, skip the non voice only ones */ 154 else /* second run, skip the non voice only ones */
155 { 155 {
156 if (!voiceonly[i] == 1) 156 if (!(voiceonly[i] == 1))
157 continue; 157 continue;
158 } 158 }
159 159