summaryrefslogtreecommitdiff
path: root/apps/radio/presets.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/radio/presets.c')
-rw-r--r--apps/radio/presets.c614
1 files changed, 614 insertions, 0 deletions
diff --git a/apps/radio/presets.c b/apps/radio/presets.c
new file mode 100644
index 0000000000..b996e68443
--- /dev/null
+++ b/apps/radio/presets.c
@@ -0,0 +1,614 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: radio.c -1 $
9 *
10 * Copyright (C) 2003 Linus Nielsen Feltzing
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "config.h"
23#include <stdio.h>
24#include <stdbool.h>
25#include <stdlib.h>
26#include "settings.h"
27#include "general.h"
28#include "radio.h"
29#include "tuner.h"
30#include "file.h"
31#include "string-extra.h"
32#include "misc.h"
33#include "lang.h"
34#include "action.h"
35#include "list.h"
36#include "splash.h"
37#include "menu.h"
38#include "yesno.h"
39#include "keyboard.h"
40#include "talk.h"
41#include "filetree.h"
42#include "dir.h"
43
44static int curr_preset = -1;
45
46extern int curr_freq; /* from radio.c.. naughty but meh */
47extern int radio_mode;
48int snap_freq_to_grid(int freq);
49void remember_frequency(void);
50void talk_freq(int freq, bool enqueue);
51
52#define MAX_PRESETS 64
53static bool presets_loaded = false, presets_changed = false;
54static struct fmstation presets[MAX_PRESETS];
55
56static char filepreset[MAX_PATH]; /* preset filename variable */
57
58static int num_presets = 0; /* The number of presets in the preset list */
59
60bool yesno_pop(const char* text); /* radio.c */
61
62int radio_current_preset(void)
63{
64 return curr_preset;
65}
66int radio_preset_count(void)
67{
68 return num_presets;
69}
70const struct fmstation *radio_get_preset(int preset)
71{
72 return &presets[preset];
73}
74
75bool has_presets_changed(void)
76{
77 return presets_changed;
78}
79
80
81/* Find a matching preset to freq */
82int find_preset(int freq)
83{
84 int i;
85 if(num_presets < 1)
86 return -1;
87 for(i = 0;i < MAX_PRESETS;i++)
88 {
89 if(freq == presets[i].frequency)
90 return i;
91 }
92
93 return -1;
94}
95
96/* Return the closest preset encountered in the search direction with
97 wraparound. */
98int find_closest_preset(int freq, int direction)
99{
100 int i;
101 int lowpreset = 0;
102 int highpreset = 0;
103 int closest = -1;
104
105 if (direction == 0) /* direction == 0 isn't really used */
106 return 0;
107
108 for (i = 0; i < num_presets; i++)
109 {
110 int f = presets[i].frequency;
111 if (f == freq)
112 return i; /* Exact match = stop */
113
114 /* remember the highest and lowest presets for wraparound */
115 if (f < presets[lowpreset].frequency)
116 lowpreset = i;
117 if (f > presets[highpreset].frequency)
118 highpreset = i;
119
120 /* find the closest preset in the given direction */
121 if (direction > 0 && f > freq)
122 {
123 if (closest < 0 || f < presets[closest].frequency)
124 closest = i;
125 }
126 else if (direction < 0 && f < freq)
127 {
128 if (closest < 0 || f > presets[closest].frequency)
129 closest = i;
130 }
131 }
132
133 if (closest < 0)
134 {
135 /* no presets in the given direction */
136 /* wrap around depending on direction */
137 if (direction < 0)
138 closest = highpreset;
139 else
140 closest = lowpreset;
141 }
142
143 return closest;
144}
145
146void next_preset(int direction)
147{
148 if (num_presets < 1)
149 return;
150
151 if (curr_preset == -1)
152 curr_preset = find_closest_preset(curr_freq, direction);
153 else
154 curr_preset = (curr_preset + direction + num_presets) % num_presets;
155
156 /* Must stay on the current grid for the region */
157 curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
158
159 tuner_set(RADIO_FREQUENCY, curr_freq);
160 remember_frequency();
161}
162
163void set_current_preset(int preset)
164{
165 curr_preset = preset;
166}
167
168/* Speak a preset by number or by spelling its name, depending on settings. */
169void talk_preset(int preset, bool fallback, bool enqueue)
170{
171 if (global_settings.talk_file == 1) /* number */
172 talk_number(preset + 1, enqueue);
173 else
174 { /* spell */
175 if(presets[preset].name[0])
176 talk_spell(presets[preset].name, enqueue);
177 else if(fallback)
178 talk_freq(presets[preset].frequency, enqueue);
179 }
180}
181
182
183void radio_save_presets(void)
184{
185 int fd;
186 int i;
187
188 fd = creat(filepreset, 0666);
189 if(fd >= 0)
190 {
191 for(i = 0;i < num_presets;i++)
192 {
193 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
194 }
195 close(fd);
196
197 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
198 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
199 presets_changed = false;
200 }
201 else
202 {
203 splash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED));
204 }
205}
206
207void radio_load_presets(char *filename)
208{
209 int fd;
210 int rc;
211 char buf[128];
212 char *freq;
213 char *name;
214 bool done = false;
215 int f;
216
217 memset(presets, 0, sizeof(presets));
218 num_presets = 0;
219
220 /* No Preset in configuration. */
221 if(filename[0] == '\0')
222 {
223 filepreset[0] = '\0';
224 return;
225 }
226 /* Temporary preset, loaded until player shuts down. */
227 else if(filename[0] == '/')
228 strlcpy(filepreset, filename, sizeof(filepreset));
229 /* Preset from default directory. */
230 else
231 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
232 FMPRESET_PATH, filename);
233
234 fd = open_utf8(filepreset, O_RDONLY);
235 if(fd >= 0)
236 {
237 while(!done && num_presets < MAX_PRESETS)
238 {
239 rc = read_line(fd, buf, 128);
240 if(rc > 0)
241 {
242 if(settings_parseline(buf, &freq, &name))
243 {
244 f = atoi(freq);
245 if(f) /* For backwards compatibility */
246 {
247 struct fmstation * const fms = &presets[num_presets];
248 fms->frequency = f;
249 strlcpy(fms->name, name, MAX_FMPRESET_LEN+1);
250 num_presets++;
251 }
252 }
253 }
254 else
255 done = true;
256 }
257 close(fd);
258 }
259 else /* invalid file name? */
260 filepreset[0] = '\0';
261
262 presets_loaded = num_presets > 0;
263 presets_changed = false;
264}
265
266const char* radio_get_preset_name(int preset)
267{
268 if (preset < num_presets)
269 return presets[preset].name;
270 return NULL;
271}
272
273int radio_add_preset(void)
274{
275 char buf[MAX_FMPRESET_LEN + 1];
276
277 if(num_presets < MAX_PRESETS)
278 {
279 buf[0] = '\0';
280
281 if (!kbd_input(buf, MAX_FMPRESET_LEN + 1))
282 {
283 struct fmstation * const fms = &presets[num_presets];
284 strcpy(fms->name, buf);
285 fms->frequency = curr_freq;
286 num_presets++;
287 presets_changed = true;
288 presets_loaded = num_presets > 0;
289 return true;
290 }
291 }
292 else
293 {
294 splash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS));
295 }
296 return false;
297}
298
299/* needed to know which preset we are edit/delete-ing */
300static int selected_preset = -1;
301static int radio_edit_preset(void)
302{
303 char buf[MAX_FMPRESET_LEN + 1];
304
305 if (num_presets > 0)
306 {
307 struct fmstation * const fms = &presets[selected_preset];
308
309 strcpy(buf, fms->name);
310
311 if (!kbd_input(buf, MAX_FMPRESET_LEN + 1))
312 {
313 strcpy(fms->name, buf);
314 presets_changed = true;
315 }
316 }
317
318 return 1;
319}
320
321static int radio_delete_preset(void)
322{
323 if (num_presets > 0)
324 {
325 struct fmstation * const fms = &presets[selected_preset];
326
327 if (selected_preset >= --num_presets)
328 selected_preset = num_presets - 1;
329
330 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
331 (uintptr_t)fms);
332
333 if (curr_preset >= num_presets)
334 --curr_preset;
335 }
336
337 /* Don't ask to save when all presets are deleted. */
338 presets_changed = num_presets > 0;
339
340 if (!presets_changed)
341 {
342 /* The preset list will be cleared, switch to Scan Mode. */
343 radio_mode = RADIO_SCAN_MODE;
344 curr_preset = -1;
345 presets_loaded = false;
346 }
347
348 return 1;
349}
350
351int load_preset_list(void)
352{
353 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
354}
355
356int save_preset_list(void)
357{
358 if(num_presets > 0)
359 {
360 bool bad_file_name = true;
361
362 if(!dir_exists(FMPRESET_PATH)) /* Check if there is preset folder */
363 mkdir(FMPRESET_PATH);
364
365 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
366 ".fmr", 2 IF_CNFN_NUM_(, NULL));
367
368 while(bad_file_name)
369 {
370 if(!kbd_input(filepreset, sizeof(filepreset)))
371 {
372 /* check the name: max MAX_FILENAME (20) chars */
373 char* p2;
374 char* p1;
375 int len;
376 p1 = strrchr(filepreset, '/');
377 p2 = p1;
378 while((p1) && (*p2) && (*p2 != '.'))
379 p2++;
380 len = (int)(p2-p1) - 1;
381 if((!p1) || (len > MAX_FILENAME) || (len == 0))
382 {
383 /* no slash, too long or too short */
384 splash(HZ, ID2P(LANG_INVALID_FILENAME));
385 }
386 else
387 {
388 /* add correct extension (easier to always write)
389 at this point, p2 points to 0 or the extension dot */
390 *p2 = '\0';
391 strcat(filepreset,".fmr");
392 bad_file_name = false;
393 radio_save_presets();
394 }
395 }
396 else
397 {
398 /* user aborted */
399 return false;
400 }
401 }
402 }
403 else
404 splash(HZ, ID2P(LANG_FM_NO_PRESETS));
405
406 return true;
407}
408
409int clear_preset_list(void)
410{
411 /* Clear all the preset entries */
412 memset(presets, 0, sizeof (presets));
413
414 num_presets = 0;
415 presets_loaded = false;
416 /* The preset list will be cleared switch to Scan Mode. */
417 radio_mode = RADIO_SCAN_MODE;
418 curr_preset = -1;
419 presets_changed = false; /* Don't ask to save when clearing the list. */
420
421 return true;
422}
423
424MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
425 ID2P(LANG_FM_EDIT_PRESET),
426 radio_edit_preset, NULL, NULL, Icon_NOICON);
427MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
428 ID2P(LANG_FM_DELETE_PRESET),
429 radio_delete_preset, NULL, NULL, Icon_NOICON);
430static int radio_preset_callback(int action,
431 const struct menu_item_ex *this_item)
432{
433 if (action == ACTION_STD_OK)
434 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
435 return action;
436 (void)this_item;
437}
438MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_PRESET),
439 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
440 &radio_delete_preset_item);
441/* present a list of preset stations */
442static const char* presets_get_name(int selected_item, void *data,
443 char *buffer, size_t buffer_len)
444{
445 (void)data;
446 struct fmstation *p = &presets[selected_item];
447 if(p->name[0])
448 return p->name;
449 int freq = p->frequency / 10000;
450 int frac = freq % 100;
451 freq /= 100;
452 snprintf(buffer, buffer_len,
453 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
454 return buffer;
455}
456
457static int presets_speak_name(int selected_item, void * data)
458{
459 (void)data;
460 talk_preset(selected_item, true, false);
461 return 0;
462}
463
464int handle_radio_presets(void)
465{
466 struct gui_synclist lists;
467 int result = 0;
468 int action = ACTION_NONE;
469#ifdef HAVE_BUTTONBAR
470 struct gui_buttonbar buttonbar;
471#endif
472
473 if(presets_loaded == false)
474 return result;
475
476#ifdef HAVE_BUTTONBAR
477 gui_buttonbar_init(&buttonbar);
478 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
479 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
480 str(LANG_FM_BUTTONBAR_EXIT),
481 str(LANG_FM_BUTTONBAR_ACTION));
482 gui_buttonbar_draw(&buttonbar);
483#endif
484 gui_synclist_init(&lists, presets_get_name, NULL, false, 1, NULL);
485 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
486 gui_synclist_set_icon_callback(&lists, NULL);
487 if(global_settings.talk_file)
488 gui_synclist_set_voice_callback(&lists, presets_speak_name);
489 gui_synclist_set_nb_items(&lists, num_presets);
490 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
491 gui_synclist_speak_item(&lists);
492
493 while (result == 0)
494 {
495 gui_synclist_draw(&lists);
496 list_do_action(CONTEXT_STD, TIMEOUT_BLOCK,
497 &lists, &action, LIST_WRAP_UNLESS_HELD);
498 switch (action)
499 {
500 case ACTION_STD_MENU:
501 if (radio_add_preset())
502 {
503 gui_synclist_set_nb_items(&lists, num_presets);
504 gui_synclist_select_item(&lists, num_presets - 1);
505 }
506 break;
507 case ACTION_STD_CANCEL:
508 result = 1;
509 break;
510 case ACTION_STD_OK:
511 curr_preset = gui_synclist_get_sel_pos(&lists);
512 curr_freq = presets[curr_preset].frequency;
513 next_station(0);
514 remember_frequency();
515 result = 1;
516 break;
517 case ACTION_F3:
518 case ACTION_STD_CONTEXT:
519 selected_preset = gui_synclist_get_sel_pos(&lists);
520 do_menu(&handle_radio_preset_menu, NULL, NULL, false);
521 gui_synclist_set_nb_items(&lists, num_presets);
522 gui_synclist_select_item(&lists, selected_preset);
523 gui_synclist_speak_item(&lists);
524 break;
525 default:
526 if(default_event_handler(action) == SYS_USB_CONNECTED)
527 result = 2;
528 }
529 }
530 return result - 1;
531}
532
533
534int scan_presets(void *viewports)
535{
536 bool do_scan = true;
537 int i;
538 struct viewport *vp = (struct viewport *)viewports;
539
540 FOR_NB_SCREENS(i)
541 screens[i].set_viewport(vp?&vp[i]:NULL);
542 if(num_presets > 0) /* Do that to avoid 2 questions. */
543 do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS));
544
545 if(do_scan)
546 {
547 const struct fm_region_data * const fmr =
548 &fm_region_data[global_settings.fm_region];
549
550 curr_freq = fmr->freq_min;
551 num_presets = 0;
552 memset(presets, 0, sizeof(presets));
553
554 tuner_set(RADIO_MUTE, 1);
555
556 while(curr_freq <= fmr->freq_max)
557 {
558 int freq, frac;
559 if(num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
560 break;
561
562 freq = curr_freq / 10000;
563 frac = freq % 100;
564 freq /= 100;
565
566 splashf(0, str(LANG_FM_SCANNING), freq, frac);
567
568 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
569 {
570 /* add preset */
571 presets[num_presets].name[0] = '\0';
572 presets[num_presets].frequency = curr_freq;
573 num_presets++;
574 }
575
576 curr_freq += fmr->freq_step;
577 }
578
579 if (get_radio_status() == FMRADIO_PLAYING)
580 tuner_set(RADIO_MUTE, 0);
581
582 presets_changed = true;
583
584 FOR_NB_SCREENS(i)
585 {
586 screens[i].clear_viewport();
587 screens[i].update_viewport();
588 }
589
590 if(num_presets > 0)
591 {
592 curr_freq = presets[0].frequency;
593 radio_mode = RADIO_PRESET_MODE;
594 presets_loaded = true;
595 next_station(0);
596 }
597 else
598 {
599 /* Wrap it to beginning or we'll be past end of band */
600 presets_loaded = false;
601 next_station(1);
602 }
603 }
604 return true;
605}
606
607
608void presets_save(void)
609{
610 if(filepreset[0] == '\0')
611 save_preset_list();
612 else
613 radio_save_presets();
614}