summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES13
-rw-r--r--apps/radio/presets.c614
-rw-r--r--apps/radio/radio.c (renamed from apps/recorder/radio.c)855
-rw-r--r--apps/radio/radio.h (renamed from apps/recorder/radio.h)15
-rw-r--r--apps/radio/radio_skin.c118
-rw-r--r--apps/radio/radioart.c180
6 files changed, 994 insertions, 801 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 98efec944a..93ec93fe27 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -132,12 +132,19 @@ recorder/albumart.c
132gui/color_picker.c 132gui/color_picker.c
133#endif 133#endif
134#endif 134#endif
135#if CONFIG_TUNER
136recorder/radio.c
137#endif
138#ifdef HAVE_RECORDING 135#ifdef HAVE_RECORDING
139recorder/recording.c 136recorder/recording.c
140#endif 137#endif
138
139#if CONFIG_TUNER
140radio/radio.c
141radio/presets.c
142radio/radio_skin.c
143#ifdef HAVE_ALBUMART
144radio/radioart.c
145#endif
146#endif
147
141#if CONFIG_CODEC == SWCODEC 148#if CONFIG_CODEC == SWCODEC
142#if INPUT_SRC_CAPS != 0 149#if INPUT_SRC_CAPS != 0
143audio_path.c 150audio_path.c
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}
diff --git a/apps/recorder/radio.c b/apps/radio/radio.c
index 5425e8a2ab..b3540610f1 100644
--- a/apps/recorder/radio.c
+++ b/apps/radio/radio.c
@@ -131,34 +131,33 @@
131 131
132#endif 132#endif
133 133
134#define RADIO_SCAN_MODE 0 134/* presets.c needs these so keep unstatic or redo the whole thing! */
135#define RADIO_PRESET_MODE 1 135int curr_freq; /* current frequency in Hz */
136 136/* these are all in presets.c... someone PLEASE rework this ! */
137static int curr_preset = -1; 137int handle_radio_presets(void);
138static int curr_freq; /* current frequency in Hz */ 138static bool radio_menu(void);
139static int radio_mode = RADIO_SCAN_MODE; 139int radio_add_preset(void);
140int save_preset_list(void);
141int load_preset_list(void);
142int clear_preset_list(void);
143void next_preset(int direction);
144void set_current_preset(int preset);
145int scan_presets(void *viewports);
146int find_preset(int freq);
147void radio_save_presets(void);
148bool has_presets_changed(void);
149void talk_preset(int preset, bool fallback, bool enqueue);
150void presets_save(void);
151
152
153
154int radio_mode = RADIO_SCAN_MODE;
140static int search_dir = 0; 155static int search_dir = 0;
141 156
142static int radio_status = FMRADIO_OFF; 157static int radio_status = FMRADIO_OFF;
143static bool in_screen = false; 158static bool in_screen = false;
144 159
145#define MAX_PRESETS 64
146static bool presets_loaded = false, presets_changed = false;
147static struct fmstation presets[MAX_PRESETS];
148
149static char filepreset[MAX_PATH]; /* preset filename variable */
150
151static int num_presets = 0; /* The number of presets in the preset list */
152
153static void radio_save_presets(void);
154static int handle_radio_presets(void);
155static bool radio_menu(void);
156static int radio_add_preset(void);
157static int save_preset_list(void);
158static int load_preset_list(void);
159static int clear_preset_list(void);
160 160
161static int scan_presets(void *viewports);
162static void radio_off(void); 161static void radio_off(void);
163 162
164bool radio_scan_mode(void) 163bool radio_scan_mode(void)
@@ -175,21 +174,9 @@ int radio_current_frequency(void)
175 return curr_freq; 174 return curr_freq;
176} 175}
177 176
178int radio_current_preset(void)
179{
180 return curr_preset;
181}
182int radio_preset_count(void)
183{
184 return num_presets;
185}
186const struct fmstation *radio_get_preset(int preset)
187{
188 return &presets[preset];
189}
190/* Function to manipulate all yesno dialogues. 177/* Function to manipulate all yesno dialogues.
191 This function needs the output text as an argument. */ 178 This function needs the output text as an argument. */
192static bool yesno_pop(const char* text) 179bool yesno_pop(const char* text)
193{ 180{
194 int i; 181 int i;
195 const char *lines[]={text}; 182 const char *lines[]={text};
@@ -200,17 +187,12 @@ static bool yesno_pop(const char* text)
200 return ret; 187 return ret;
201} 188}
202 189
203#if defined(HAVE_RECORDING) && defined(HAVE_ALBUMART)
204static void recording_started_handler(void *data);
205static void recording_stopped_handler(void *data);
206#endif
207void radio_init(void) 190void radio_init(void)
208{ 191{
209 tuner_init(); 192 tuner_init();
210 radio_off(); 193 radio_off();
211#if defined(HAVE_RECORDING) && defined(HAVE_ALBUMART) 194#ifdef HAVE_ALBUMART
212 add_event(RECORDING_EVENT_START, false, recording_started_handler); 195 radioart_init(false);
213 add_event(RECORDING_EVENT_STOP, false, recording_stopped_handler);
214#endif 196#endif
215} 197}
216 198
@@ -324,7 +306,7 @@ bool radio_hardware_present(void)
324} 306}
325 307
326/* Keep freq on the grid for the current region */ 308/* Keep freq on the grid for the current region */
327static int snap_freq_to_grid(int freq) 309int snap_freq_to_grid(int freq)
328{ 310{
329 const struct fm_region_data * const fmr = 311 const struct fm_region_data * const fmr =
330 &fm_region_data[global_settings.fm_region]; 312 &fm_region_data[global_settings.fm_region];
@@ -341,72 +323,7 @@ static int snap_freq_to_grid(int freq)
341 return freq; 323 return freq;
342} 324}
343 325
344/* Find a matching preset to freq */ 326void remember_frequency(void)
345static int find_preset(int freq)
346{
347 int i;
348 if(num_presets < 1)
349 return -1;
350 for(i = 0;i < MAX_PRESETS;i++)
351 {
352 if(freq == presets[i].frequency)
353 return i;
354 }
355
356 return -1;
357}
358
359/* Return the closest preset encountered in the search direction with
360 wraparound. */
361static int find_closest_preset(int freq, int direction)
362{
363 int i;
364 int lowpreset = 0;
365 int highpreset = 0;
366 int closest = -1;
367
368 if (direction == 0) /* direction == 0 isn't really used */
369 return 0;
370
371 for (i = 0; i < num_presets; i++)
372 {
373 int f = presets[i].frequency;
374 if (f == freq)
375 return i; /* Exact match = stop */
376
377 /* remember the highest and lowest presets for wraparound */
378 if (f < presets[lowpreset].frequency)
379 lowpreset = i;
380 if (f > presets[highpreset].frequency)
381 highpreset = i;
382
383 /* find the closest preset in the given direction */
384 if (direction > 0 && f > freq)
385 {
386 if (closest < 0 || f < presets[closest].frequency)
387 closest = i;
388 }
389 else if (direction < 0 && f < freq)
390 {
391 if (closest < 0 || f > presets[closest].frequency)
392 closest = i;
393 }
394 }
395
396 if (closest < 0)
397 {
398 /* no presets in the given direction */
399 /* wrap around depending on direction */
400 if (direction < 0)
401 closest = highpreset;
402 else
403 closest = lowpreset;
404 }
405
406 return closest;
407}
408
409static void remember_frequency(void)
410{ 327{
411 const struct fm_region_data * const fmr = 328 const struct fm_region_data * const fmr =
412 &fm_region_data[global_settings.fm_region]; 329 &fm_region_data[global_settings.fm_region];
@@ -415,23 +332,6 @@ static void remember_frequency(void)
415 status_save(); 332 status_save();
416} 333}
417 334
418static void next_preset(int direction)
419{
420 if (num_presets < 1)
421 return;
422
423 if (curr_preset == -1)
424 curr_preset = find_closest_preset(curr_freq, direction);
425 else
426 curr_preset = (curr_preset + direction + num_presets) % num_presets;
427
428 /* Must stay on the current grid for the region */
429 curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
430
431 tuner_set(RADIO_FREQUENCY, curr_freq);
432 remember_frequency();
433}
434
435/* Step to the next or previous frequency */ 335/* Step to the next or previous frequency */
436static int step_freq(int freq, int direction) 336static int step_freq(int freq, int direction)
437{ 337{
@@ -452,7 +352,7 @@ static int step_freq(int freq, int direction)
452} 352}
453 353
454/* Step to the next or previous station */ 354/* Step to the next or previous station */
455static void next_station(int direction) 355void next_station(int direction)
456{ 356{
457 if (direction != 0 && radio_mode != RADIO_SCAN_MODE) 357 if (direction != 0 && radio_mode != RADIO_SCAN_MODE)
458 { 358 {
@@ -470,7 +370,7 @@ static void next_station(int direction)
470 if (radio_status == FMRADIO_PLAYING) 370 if (radio_status == FMRADIO_PLAYING)
471 tuner_set(RADIO_MUTE, 0); 371 tuner_set(RADIO_MUTE, 0);
472 372
473 curr_preset = find_preset(curr_freq); 373 set_current_preset(find_preset(curr_freq));
474 remember_frequency(); 374 remember_frequency();
475} 375}
476 376
@@ -483,7 +383,7 @@ static void end_search(void)
483} 383}
484 384
485/* Speak a frequency. */ 385/* Speak a frequency. */
486static void talk_freq(int freq, bool enqueue) 386void talk_freq(int freq, bool enqueue)
487{ 387{
488 freq /= 10000; 388 freq /= 10000;
489 talk_number(freq / 100, enqueue); 389 talk_number(freq / 100, enqueue);
@@ -493,224 +393,6 @@ static void talk_freq(int freq, bool enqueue)
493 talk_number(freq % 10, true); 393 talk_number(freq % 10, true);
494} 394}
495 395
496/* Speak a preset by number or by spelling its name, depending on settings. */
497static void talk_preset(int preset, bool fallback, bool enqueue)
498{
499 if (global_settings.talk_file == 1) /* number */
500 talk_number(preset + 1, enqueue);
501 else
502 { /* spell */
503 if(presets[preset].name[0])
504 talk_spell(presets[preset].name, enqueue);
505 else if(fallback)
506 talk_freq(presets[preset].frequency, enqueue);
507 }
508}
509
510/* Skin stuff */
511extern struct wps_state wps_state; /* from wps.c */
512static struct gui_wps fms_skin[NB_SCREENS] = {{ .data = NULL }};
513static struct wps_data fms_skin_data[NB_SCREENS] = {{ .wps_loaded = 0 }};
514static struct wps_sync_data fms_skin_sync_data = { .do_full_update = false };
515
516#ifdef HAVE_ALBUMART
517#define MAX_RADIOART_IMAGES 10
518struct radioart {
519 int handle;
520 long last_tick;
521 struct dim dim;
522 char name[MAX_FMPRESET_LEN+1];
523};
524
525static struct radioart radioart[MAX_RADIOART_IMAGES];
526#ifdef HAVE_RECORDING
527static bool allow_buffer_access = true; /* If we are recording dont touch the buffers! */
528#endif
529static int find_oldest_image(void)
530{
531 int i;
532 long oldest_tick = radioart[0].last_tick;
533 int oldest_idx = 0;
534 for(i=1;i<MAX_RADIOART_IMAGES;i++)
535 {
536 if (radioart[i].last_tick < oldest_tick)
537 {
538 oldest_tick = radioart[i].last_tick;
539 oldest_idx = i;
540 }
541 }
542 return oldest_idx;
543}
544static int load_radioart_image(struct radioart *ra, char* preset_name, struct dim *dim)
545{
546 char path[MAX_PATH];
547#ifndef HAVE_NOISY_IDLE_MODE
548 cpu_idle_mode(false);
549#endif
550 snprintf(path, sizeof(path), FMPRESET_PATH "/%s.bmp",preset_name);
551 if (!file_exists(path))
552 snprintf(path, sizeof(path), FMPRESET_PATH "/%s.jpg",preset_name);
553 if (!file_exists(path))
554 {
555#ifndef HAVE_NOISY_IDLE_MODE
556 cpu_idle_mode(true);
557#endif
558 return -1;
559 }
560 strlcpy(ra->name, preset_name, MAX_FMPRESET_LEN+1);
561 ra->dim.height = dim->height;
562 ra->dim.width = dim->width;
563 ra->last_tick = current_tick;
564 ra->handle = bufopen(path, 0, TYPE_BITMAP, &ra->dim);
565 if (ra->handle == ERR_BUFFER_FULL)
566 {
567 int i = find_oldest_image();
568 bufclose(i);
569 ra->handle = bufopen(path, 0, TYPE_BITMAP, &ra->dim);
570 }
571#ifndef HAVE_NOISY_IDLE_MODE
572 cpu_idle_mode(true);
573#endif
574 return ra->handle;
575}
576int radio_get_art_hid(struct dim *requested_dim)
577{
578 int preset = radio_current_preset();
579 int i, free_idx = -1;
580 if ((radio_mode != RADIO_PRESET_MODE) || preset < 0)
581 return -1;
582#ifdef HAVE_RECORDING
583 if (!allow_buffer_access)
584 return -1;
585#endif
586 for(i=0;i<MAX_RADIOART_IMAGES;i++)
587 {
588 if (radioart[i].handle < 0)
589 {
590 free_idx = i;
591 }
592 else if (!strcmp(radioart[i].name, presets[preset].name) &&
593 radioart[i].dim.width == requested_dim->width &&
594 radioart[i].dim.height == requested_dim->height)
595 {
596 radioart[i].last_tick = current_tick;
597 return radioart[i].handle;
598 }
599 }
600 if (free_idx >= 0)
601 {
602 return load_radioart_image(&radioart[free_idx],
603 presets[preset].name, requested_dim);
604 }
605 else
606 {
607 int i = find_oldest_image();
608 bufclose(radioart[i].handle);
609 return load_radioart_image(&radioart[i],
610 presets[preset].name, requested_dim);
611 }
612
613 return -1;
614}
615static void playback_restarting_handler(void *data)
616{
617 (void)data;
618 int i;
619 for(i=0;i<MAX_RADIOART_IMAGES;i++)
620 {
621 if (radioart[i].handle >= 0)
622 bufclose(radioart[i].handle);
623 radioart[i].handle = -1;
624 radioart[i].name[0] = '\0';
625 }
626}
627#ifdef HAVE_RECORDING
628static void recording_started_handler(void *data)
629{
630 (void)data;
631 allow_buffer_access = false;
632 playback_restarting_handler(NULL);
633}
634static void recording_stopped_handler(void *data)
635{
636 (void)data;
637 allow_buffer_access = true;
638}
639#endif
640#endif
641
642void fms_data_load(enum screen_type screen, const char *buf, bool isfile)
643{
644 struct wps_data *data = fms_skin[screen].data;
645 int success;
646 success = buf && skin_data_load(screen, data, buf, isfile);
647
648 if (!success ) /* load the default */
649 {
650 const char default_fms[] = "%Sx|Station:| %tf\n"
651 "%?ts<%Sx|Stereo||%Sx|Mono|>\n"
652 "%?tm<%Sx|Mode:| %Sx|Scan||%Sx|Preset|: %Ti. %?Tn<%Tn|%Tf>>\n"
653 "%pb\n"
654#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
655 "%?Rr<%Sx|Time:| %Rh:%Rn:%Rs|"
656 "%?St|prerecording time|<%Sx|Prerecord Time| %Rs|%pm>>\n"
657#endif
658#ifdef HAVE_RDS_CAP
659 "\n%s%ty\n"
660 "%s%tz\n"
661#endif
662 ;
663 skin_data_load(screen, data, default_fms, false);
664 }
665}
666enum fms_exiting {
667 FMS_EXIT,
668 FMS_ENTER
669};
670void fms_fix_displays(enum fms_exiting toggle_state)
671{
672 int i;
673 FOR_NB_SCREENS(i)
674 {
675 if (toggle_state == FMS_ENTER)
676 {
677 viewportmanager_theme_enable(i, skin_has_sbs(i, fms_skin[i].data), NULL);
678#if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
679 screens[i].backdrop_show(fms_skin[i].data->backdrop);
680#endif
681 screens[i].clear_display();
682 /* force statusbar/skin update since we just cleared the whole screen */
683 send_event(GUI_EVENT_ACTIONUPDATE, (void*)1);
684 }
685 else
686 {
687 screens[i].stop_scroll();
688#if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
689 screens[i].backdrop_show(sb_get_backdrop(i));
690#endif
691 viewportmanager_theme_undo(i, skin_has_sbs(i, fms_skin[i].data));
692 }
693 }
694}
695
696
697void fms_skin_init(void)
698{
699 int i;
700 FOR_NB_SCREENS(i)
701 {
702#ifdef HAVE_ALBUMART
703 fms_skin_data[i].albumart = NULL;
704 fms_skin_data[i].playback_aa_slot = -1;
705#endif
706 fms_skin[i].data = &fms_skin_data[i];
707 fms_skin[i].display = &screens[i];
708 /* Currently no seperate wps_state needed/possible
709 so use the only available ( "global" ) one */
710 fms_skin[i].state = &wps_state;
711 fms_skin[i].sync_data = &fms_skin_sync_data;
712 }
713}
714 396
715int radio_screen(void) 397int radio_screen(void)
716{ 398{
@@ -743,17 +425,12 @@ int radio_screen(void)
743 /* change status to "in screen" */ 425 /* change status to "in screen" */
744 in_screen = true; 426 in_screen = true;
745 427
746 if(num_presets <= 0) 428 if(radio_preset_count() <= 0)
747 { 429 {
748 radio_load_presets(global_settings.fmr_file); 430 radio_load_presets(global_settings.fmr_file);
749 } 431 }
750#ifdef HAVE_ALBUMART 432#ifdef HAVE_ALBUMART
751 for(i=0;i<MAX_RADIOART_IMAGES;i++) 433 radioart_init(true);
752 {
753 radioart[i].handle = -1;
754 radioart[i].name[0] = '\0';
755 }
756 add_event(PLAYBACK_EVENT_START_PLAYBACK, true, playback_restarting_handler);
757#endif 434#endif
758 435
759 if(radio_status == FMRADIO_OFF) 436 if(radio_status == FMRADIO_OFF)
@@ -793,11 +470,11 @@ int radio_screen(void)
793 radio_start(); 470 radio_start();
794#endif 471#endif
795 472
796 if(num_presets < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN))) 473 if(radio_preset_count() < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN)))
797 scan_presets(NULL); 474 scan_presets(NULL);
798 475
799 curr_preset = find_preset(curr_freq); 476 set_current_preset(find_preset(curr_freq));
800 if(curr_preset != -1) 477 if(radio_current_preset() != -1)
801 radio_mode = RADIO_PRESET_MODE; 478 radio_mode = RADIO_PRESET_MODE;
802 479
803#ifndef HAVE_NOISY_IDLE_MODE 480#ifndef HAVE_NOISY_IDLE_MODE
@@ -813,7 +490,7 @@ int radio_screen(void)
813 490
814 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq)) 491 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
815 { 492 {
816 curr_preset = find_preset(curr_freq); 493 set_current_preset(find_preset(curr_freq));
817 remember_frequency(); 494 remember_frequency();
818 end_search(); 495 end_search();
819 talk = true; 496 talk = true;
@@ -826,8 +503,7 @@ int radio_screen(void)
826 cancel_cpu_boost(); 503 cancel_cpu_boost();
827 } 504 }
828 505
829 button = skin_wait_for_action(fms_skin, CONTEXT_FM, 506 button = fms_do_button_loop(update_screen);
830 update_screen ? TIMEOUT_NOBLOCK : HZ);
831 507
832#ifndef HAVE_NOISY_IDLE_MODE 508#ifndef HAVE_NOISY_IDLE_MODE
833 if (button != ACTION_NONE) 509 if (button != ACTION_NONE)
@@ -848,14 +524,11 @@ int radio_screen(void)
848#endif 524#endif
849 { 525 {
850 done = true; 526 done = true;
851 if(presets_changed) 527 if(has_presets_changed())
852 { 528 {
853 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES))) 529 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
854 { 530 {
855 if(filepreset[0] == '\0') 531 presets_save();
856 save_preset_list();
857 else
858 radio_save_presets();
859 } 532 }
860 } 533 }
861 } 534 }
@@ -901,14 +574,11 @@ int radio_screen(void)
901 keep_playing = true; 574 keep_playing = true;
902 done = true; 575 done = true;
903 ret_val = GO_TO_ROOT; 576 ret_val = GO_TO_ROOT;
904 if(presets_changed) 577 if(has_presets_changed())
905 { 578 {
906 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES))) 579 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
907 { 580 {
908 if(filepreset[0] == '\0') 581 presets_save();
909 save_preset_list();
910 else
911 radio_save_presets();
912 } 582 }
913 } 583 }
914 584
@@ -971,14 +641,14 @@ int radio_screen(void)
971 case ACTION_FM_MENU: 641 case ACTION_FM_MENU:
972 fms_fix_displays(FMS_EXIT); 642 fms_fix_displays(FMS_EXIT);
973 radio_menu(); 643 radio_menu();
974 curr_preset = find_preset(curr_freq); 644 set_current_preset(find_preset(curr_freq));
975 update_screen = true; 645 update_screen = true;
976 restore = true; 646 restore = true;
977 break; 647 break;
978 648
979#ifdef FM_PRESET 649#ifdef FM_PRESET
980 case ACTION_FM_PRESET: 650 case ACTION_FM_PRESET:
981 if(num_presets < 1) 651 if(radio_preset_count() < 1)
982 { 652 {
983 splash(HZ, ID2P(LANG_FM_NO_PRESETS)); 653 splash(HZ, ID2P(LANG_FM_NO_PRESETS));
984 update_screen = true; 654 update_screen = true;
@@ -1024,7 +694,7 @@ int radio_screen(void)
1024 if(radio_mode == RADIO_SCAN_MODE) 694 if(radio_mode == RADIO_SCAN_MODE)
1025 { 695 {
1026 /* Force scan mode if there are no presets. */ 696 /* Force scan mode if there are no presets. */
1027 if(num_presets > 0) 697 if(radio_preset_count() > 0)
1028 radio_mode = RADIO_PRESET_MODE; 698 radio_mode = RADIO_PRESET_MODE;
1029 } 699 }
1030 else 700 else
@@ -1069,14 +739,11 @@ int radio_screen(void)
1069 keep_playing = false; 739 keep_playing = false;
1070 done = true; 740 done = true;
1071 ret_val = GO_TO_ROOT; 741 ret_val = GO_TO_ROOT;
1072 if(presets_changed) 742 if(has_presets_changed())
1073 { 743 {
1074 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES))) 744 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
1075 { 745 {
1076 if(filepreset[0] == '\0') 746 radio_save_presets();
1077 save_preset_list();
1078 else
1079 radio_save_presets();
1080 } 747 }
1081 } 748 }
1082 749
@@ -1132,7 +799,7 @@ int radio_screen(void)
1132 if (restore) 799 if (restore)
1133 fms_fix_displays(FMS_ENTER); 800 fms_fix_displays(FMS_ENTER);
1134 FOR_NB_SCREENS(i) 801 FOR_NB_SCREENS(i)
1135 skin_update(&fms_skin[i], WPS_REFRESH_ALL); 802 skin_update(fms_get(i), WPS_REFRESH_ALL);
1136 restore = false; 803 restore = false;
1137 } 804 }
1138 } 805 }
@@ -1148,8 +815,8 @@ int radio_screen(void)
1148 talk_freq(curr_freq, enqueue); 815 talk_freq(curr_freq, enqueue);
1149 enqueue = true; 816 enqueue = true;
1150 } 817 }
1151 if (curr_preset >= 0) 818 if (radio_current_preset() >= 0)
1152 talk_preset(curr_preset, radio_mode == RADIO_PRESET_MODE, 819 talk_preset(radio_current_preset(), radio_mode == RADIO_PRESET_MODE,
1153 enqueue); 820 enqueue);
1154 } 821 }
1155 822
@@ -1224,350 +891,6 @@ int radio_screen(void)
1224#endif 891#endif
1225} /* radio_screen */ 892} /* radio_screen */
1226 893
1227static void radio_save_presets(void)
1228{
1229 int fd;
1230 int i;
1231
1232 fd = creat(filepreset, 0666);
1233 if(fd >= 0)
1234 {
1235 for(i = 0;i < num_presets;i++)
1236 {
1237 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
1238 }
1239 close(fd);
1240
1241 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
1242 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
1243 presets_changed = false;
1244 }
1245 else
1246 {
1247 splash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED));
1248 }
1249}
1250
1251void radio_load_presets(char *filename)
1252{
1253 int fd;
1254 int rc;
1255 char buf[128];
1256 char *freq;
1257 char *name;
1258 bool done = false;
1259 int f;
1260
1261 memset(presets, 0, sizeof(presets));
1262 num_presets = 0;
1263
1264 /* No Preset in configuration. */
1265 if(filename[0] == '\0')
1266 {
1267 filepreset[0] = '\0';
1268 return;
1269 }
1270 /* Temporary preset, loaded until player shuts down. */
1271 else if(filename[0] == '/')
1272 strlcpy(filepreset, filename, sizeof(filepreset));
1273 /* Preset from default directory. */
1274 else
1275 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1276 FMPRESET_PATH, filename);
1277
1278 fd = open_utf8(filepreset, O_RDONLY);
1279 if(fd >= 0)
1280 {
1281 while(!done && num_presets < MAX_PRESETS)
1282 {
1283 rc = read_line(fd, buf, 128);
1284 if(rc > 0)
1285 {
1286 if(settings_parseline(buf, &freq, &name))
1287 {
1288 f = atoi(freq);
1289 if(f) /* For backwards compatibility */
1290 {
1291 struct fmstation * const fms = &presets[num_presets];
1292 fms->frequency = f;
1293 strlcpy(fms->name, name, MAX_FMPRESET_LEN+1);
1294 num_presets++;
1295 }
1296 }
1297 }
1298 else
1299 done = true;
1300 }
1301 close(fd);
1302 }
1303 else /* invalid file name? */
1304 filepreset[0] = '\0';
1305
1306 presets_loaded = num_presets > 0;
1307 presets_changed = false;
1308}
1309
1310
1311static int radio_add_preset(void)
1312{
1313 char buf[MAX_FMPRESET_LEN + 1];
1314
1315 if(num_presets < MAX_PRESETS)
1316 {
1317 buf[0] = '\0';
1318
1319 if (!kbd_input(buf, MAX_FMPRESET_LEN + 1))
1320 {
1321 struct fmstation * const fms = &presets[num_presets];
1322 strcpy(fms->name, buf);
1323 fms->frequency = curr_freq;
1324 num_presets++;
1325 presets_changed = true;
1326 presets_loaded = num_presets > 0;
1327 return true;
1328 }
1329 }
1330 else
1331 {
1332 splash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS));
1333 }
1334 return false;
1335}
1336
1337/* needed to know which preset we are edit/delete-ing */
1338static int selected_preset = -1;
1339static int radio_edit_preset(void)
1340{
1341 char buf[MAX_FMPRESET_LEN + 1];
1342
1343 if (num_presets > 0)
1344 {
1345 struct fmstation * const fms = &presets[selected_preset];
1346
1347 strcpy(buf, fms->name);
1348
1349 if (!kbd_input(buf, MAX_FMPRESET_LEN + 1))
1350 {
1351 strcpy(fms->name, buf);
1352 presets_changed = true;
1353 }
1354 }
1355
1356 return 1;
1357}
1358
1359static int radio_delete_preset(void)
1360{
1361 if (num_presets > 0)
1362 {
1363 struct fmstation * const fms = &presets[selected_preset];
1364
1365 if (selected_preset >= --num_presets)
1366 selected_preset = num_presets - 1;
1367
1368 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1369 (uintptr_t)fms);
1370
1371 if (curr_preset >= num_presets)
1372 --curr_preset;
1373 }
1374
1375 /* Don't ask to save when all presets are deleted. */
1376 presets_changed = num_presets > 0;
1377
1378 if (!presets_changed)
1379 {
1380 /* The preset list will be cleared, switch to Scan Mode. */
1381 radio_mode = RADIO_SCAN_MODE;
1382 curr_preset = -1;
1383 presets_loaded = false;
1384 }
1385
1386 return 1;
1387}
1388
1389static int load_preset_list(void)
1390{
1391 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1392}
1393
1394static int save_preset_list(void)
1395{
1396 if(num_presets > 0)
1397 {
1398 bool bad_file_name = true;
1399
1400 if(!dir_exists(FMPRESET_PATH)) /* Check if there is preset folder */
1401 mkdir(FMPRESET_PATH);
1402
1403 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1404 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1405
1406 while(bad_file_name)
1407 {
1408 if(!kbd_input(filepreset, sizeof(filepreset)))
1409 {
1410 /* check the name: max MAX_FILENAME (20) chars */
1411 char* p2;
1412 char* p1;
1413 int len;
1414 p1 = strrchr(filepreset, '/');
1415 p2 = p1;
1416 while((p1) && (*p2) && (*p2 != '.'))
1417 p2++;
1418 len = (int)(p2-p1) - 1;
1419 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1420 {
1421 /* no slash, too long or too short */
1422 splash(HZ, ID2P(LANG_INVALID_FILENAME));
1423 }
1424 else
1425 {
1426 /* add correct extension (easier to always write)
1427 at this point, p2 points to 0 or the extension dot */
1428 *p2 = '\0';
1429 strcat(filepreset,".fmr");
1430 bad_file_name = false;
1431 radio_save_presets();
1432 }
1433 }
1434 else
1435 {
1436 /* user aborted */
1437 return false;
1438 }
1439 }
1440 }
1441 else
1442 splash(HZ, ID2P(LANG_FM_NO_PRESETS));
1443
1444 return true;
1445}
1446
1447static int clear_preset_list(void)
1448{
1449 /* Clear all the preset entries */
1450 memset(presets, 0, sizeof (presets));
1451
1452 num_presets = 0;
1453 presets_loaded = false;
1454 /* The preset list will be cleared switch to Scan Mode. */
1455 radio_mode = RADIO_SCAN_MODE;
1456 curr_preset = -1;
1457 presets_changed = false; /* Don't ask to save when clearing the list. */
1458
1459 return true;
1460}
1461
1462MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1463 ID2P(LANG_FM_EDIT_PRESET),
1464 radio_edit_preset, NULL, NULL, Icon_NOICON);
1465MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1466 ID2P(LANG_FM_DELETE_PRESET),
1467 radio_delete_preset, NULL, NULL, Icon_NOICON);
1468static int radio_preset_callback(int action,
1469 const struct menu_item_ex *this_item)
1470{
1471 if (action == ACTION_STD_OK)
1472 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1473 return action;
1474 (void)this_item;
1475}
1476MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_PRESET),
1477 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1478 &radio_delete_preset_item);
1479/* present a list of preset stations */
1480static const char* presets_get_name(int selected_item, void *data,
1481 char *buffer, size_t buffer_len)
1482{
1483 (void)data;
1484 struct fmstation *p = &presets[selected_item];
1485 if(p->name[0])
1486 return p->name;
1487 int freq = p->frequency / 10000;
1488 int frac = freq % 100;
1489 freq /= 100;
1490 snprintf(buffer, buffer_len,
1491 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1492 return buffer;
1493}
1494
1495static int presets_speak_name(int selected_item, void * data)
1496{
1497 (void)data;
1498 talk_preset(selected_item, true, false);
1499 return 0;
1500}
1501
1502static int handle_radio_presets(void)
1503{
1504 struct gui_synclist lists;
1505 int result = 0;
1506 int action = ACTION_NONE;
1507#ifdef HAVE_BUTTONBAR
1508 struct gui_buttonbar buttonbar;
1509#endif
1510
1511 if(presets_loaded == false)
1512 return result;
1513
1514#ifdef HAVE_BUTTONBAR
1515 gui_buttonbar_init(&buttonbar);
1516 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1517 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1518 str(LANG_FM_BUTTONBAR_EXIT),
1519 str(LANG_FM_BUTTONBAR_ACTION));
1520 gui_buttonbar_draw(&buttonbar);
1521#endif
1522 gui_synclist_init(&lists, presets_get_name, NULL, false, 1, NULL);
1523 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
1524 gui_synclist_set_icon_callback(&lists, NULL);
1525 if(global_settings.talk_file)
1526 gui_synclist_set_voice_callback(&lists, presets_speak_name);
1527 gui_synclist_set_nb_items(&lists, num_presets);
1528 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1529 gui_synclist_speak_item(&lists);
1530
1531 while (result == 0)
1532 {
1533 gui_synclist_draw(&lists);
1534 list_do_action(CONTEXT_STD, TIMEOUT_BLOCK,
1535 &lists, &action, LIST_WRAP_UNLESS_HELD);
1536 switch (action)
1537 {
1538 case ACTION_STD_MENU:
1539 if (radio_add_preset())
1540 {
1541 gui_synclist_set_nb_items(&lists, num_presets);
1542 gui_synclist_select_item(&lists, num_presets - 1);
1543 }
1544 break;
1545 case ACTION_STD_CANCEL:
1546 result = 1;
1547 break;
1548 case ACTION_STD_OK:
1549 curr_preset = gui_synclist_get_sel_pos(&lists);
1550 curr_freq = presets[curr_preset].frequency;
1551 next_station(0);
1552 remember_frequency();
1553 result = 1;
1554 break;
1555 case ACTION_F3:
1556 case ACTION_STD_CONTEXT:
1557 selected_preset = gui_synclist_get_sel_pos(&lists);
1558 do_menu(&handle_radio_preset_menu, NULL, NULL, false);
1559 gui_synclist_set_nb_items(&lists, num_presets);
1560 gui_synclist_select_item(&lists, selected_preset);
1561 gui_synclist_speak_item(&lists);
1562 break;
1563 default:
1564 if(default_event_handler(action) == SYS_USB_CONNECTED)
1565 result = 2;
1566 }
1567 }
1568 return result - 1;
1569}
1570
1571void toggle_mono_mode(bool mono) 894void toggle_mono_mode(bool mono)
1572{ 895{
1573 tuner_set(RADIO_FORCE_MONO, mono); 896 tuner_set(RADIO_FORCE_MONO, mono);
@@ -1607,78 +930,6 @@ MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1607 get_mode_text, NULL, NULL, NULL, Icon_NOICON); 930 get_mode_text, NULL, NULL, NULL, Icon_NOICON);
1608#endif 931#endif
1609 932
1610static int scan_presets(void *viewports)
1611{
1612 bool do_scan = true;
1613 int i;
1614 struct viewport *vp = (struct viewport *)viewports;
1615
1616 FOR_NB_SCREENS(i)
1617 screens[i].set_viewport(vp?&vp[i]:NULL);
1618 if(num_presets > 0) /* Do that to avoid 2 questions. */
1619 do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS));
1620
1621 if(do_scan)
1622 {
1623 const struct fm_region_data * const fmr =
1624 &fm_region_data[global_settings.fm_region];
1625
1626 curr_freq = fmr->freq_min;
1627 num_presets = 0;
1628 memset(presets, 0, sizeof(presets));
1629
1630 tuner_set(RADIO_MUTE, 1);
1631
1632 while(curr_freq <= fmr->freq_max)
1633 {
1634 int freq, frac;
1635 if(num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1636 break;
1637
1638 freq = curr_freq / 10000;
1639 frac = freq % 100;
1640 freq /= 100;
1641
1642 splashf(0, str(LANG_FM_SCANNING), freq, frac);
1643
1644 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
1645 {
1646 /* add preset */
1647 presets[num_presets].name[0] = '\0';
1648 presets[num_presets].frequency = curr_freq;
1649 num_presets++;
1650 }
1651
1652 curr_freq += fmr->freq_step;
1653 }
1654
1655 if (radio_status == FMRADIO_PLAYING)
1656 tuner_set(RADIO_MUTE, 0);
1657
1658 presets_changed = true;
1659
1660 FOR_NB_SCREENS(i)
1661 {
1662 screens[i].clear_viewport();
1663 screens[i].update_viewport();
1664 }
1665
1666 if(num_presets > 0)
1667 {
1668 curr_freq = presets[0].frequency;
1669 radio_mode = RADIO_PRESET_MODE;
1670 presets_loaded = true;
1671 next_station(0);
1672 }
1673 else
1674 {
1675 /* Wrap it to beginning or we'll be past end of band */
1676 presets_loaded = false;
1677 next_station(1);
1678 }
1679 }
1680 return true;
1681}
1682 933
1683 934
1684#ifdef HAVE_RECORDING 935#ifdef HAVE_RECORDING
@@ -1733,10 +984,18 @@ MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1733 fm_recording_settings, NULL, NULL, Icon_Recording); 984 fm_recording_settings, NULL, NULL, Icon_Recording);
1734#endif 985#endif
1735#ifndef FM_PRESET 986#ifndef FM_PRESET
987int handle_radio_presets_menu(void)
988{
989 return handle_radio_presets();
990}
1736MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET), 991MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
1737 handle_radio_presets, NULL, NULL, Icon_NOICON); 992 handle_radio_presets_menu, NULL, NULL, Icon_NOICON);
1738#endif 993#endif
1739#ifndef FM_PRESET_ADD 994#ifndef FM_PRESET_ADD
995int handle_radio_addpreset_menu(void)
996{
997 return radio_add_preset();
998}
1740MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET), 999MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1741 radio_add_preset, NULL, NULL, Icon_NOICON); 1000 radio_add_preset, NULL, NULL, Icon_NOICON);
1742#endif 1001#endif
diff --git a/apps/recorder/radio.h b/apps/radio/radio.h
index db23ee5e84..f7b0a0e80b 100644
--- a/apps/recorder/radio.h
+++ b/apps/radio/radio.h
@@ -27,6 +27,11 @@
27#include "screen_access.h" 27#include "screen_access.h"
28#include "bmp.h" 28#include "bmp.h"
29 29
30enum {
31 RADIO_SCAN_MODE = 0,
32 RADIO_PRESET_MODE,
33};
34
30#if CONFIG_TUNER 35#if CONFIG_TUNER
31void radio_load_presets(char *filename); 36void radio_load_presets(char *filename);
32void radio_init(void) INIT_ATTR; 37void radio_init(void) INIT_ATTR;
@@ -59,11 +64,21 @@ struct fmstation
59 int frequency; /* In Hz */ 64 int frequency; /* In Hz */
60 char name[MAX_FMPRESET_LEN+1]; 65 char name[MAX_FMPRESET_LEN+1];
61}; 66};
67const char* radio_get_preset_name(int preset);
62 68
63#ifdef HAVE_ALBUMART 69#ifdef HAVE_ALBUMART
70void radioart_init(bool entering_screen);
64int radio_get_art_hid(struct dim *requested_dim); 71int radio_get_art_hid(struct dim *requested_dim);
65#endif 72#endif
66 73
74void next_station(int direction);
75
76
77enum fms_exiting {
78 FMS_EXIT,
79 FMS_ENTER
80};
81
67#endif /* CONFIG_TUNER */ 82#endif /* CONFIG_TUNER */
68 83
69#endif /* RADIO_H */ 84#endif /* RADIO_H */
diff --git a/apps/radio/radio_skin.c b/apps/radio/radio_skin.c
new file mode 100644
index 0000000000..f9713703ac
--- /dev/null
+++ b/apps/radio/radio_skin.c
@@ -0,0 +1,118 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: radio.c -1 $
9 *
10 * Copyright (C) 2010 Jonathan Gordon
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
23#include "config.h"
24#include <stdio.h>
25#include <stdbool.h>
26#include <stdlib.h>
27#include "skin_engine/skin_engine.h"
28#include "settings.h"
29#include "radio.h"
30#include "action.h"
31#include "appevents.h"
32#include "statusbar-skinned.h"
33
34
35extern struct wps_state wps_state; /* from wps.c */
36static struct gui_wps fms_skin[NB_SCREENS] = {{ .data = NULL }};
37static struct wps_data fms_skin_data[NB_SCREENS] = {{ .wps_loaded = 0 }};
38static struct wps_sync_data fms_skin_sync_data = { .do_full_update = false };
39
40void fms_data_load(enum screen_type screen, const char *buf, bool isfile)
41{
42 struct wps_data *data = fms_skin[screen].data;
43 int success;
44 success = buf && skin_data_load(screen, data, buf, isfile);
45
46 if (!success ) /* load the default */
47 {
48 const char default_fms[] = "%Sx|Station:| %tf\n"
49 "%?ts<%Sx|Stereo||%Sx|Mono|>\n"
50 "%?tm<%Sx|Mode:| %Sx|Scan||%Sx|Preset|: %Ti. %?Tn<%Tn|%Tf>>\n"
51 "%pb\n"
52#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
53 "%?Rr<%Sx|Time:| %Rh:%Rn:%Rs|"
54 "%?St|prerecording time|<%Sx|Prerecord Time| %Rs|%pm>>\n"
55#endif
56#ifdef HAVE_RDS_CAP
57 "\n%s%ty\n"
58 "%s%tz\n"
59#endif
60 ;
61 skin_data_load(screen, data, default_fms, false);
62 }
63}
64void fms_fix_displays(enum fms_exiting toggle_state)
65{
66 int i;
67 FOR_NB_SCREENS(i)
68 {
69 if (toggle_state == FMS_ENTER)
70 {
71 viewportmanager_theme_enable(i, skin_has_sbs(i, fms_skin[i].data), NULL);
72#if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
73 screens[i].backdrop_show(fms_skin[i].data->backdrop);
74#endif
75 screens[i].clear_display();
76 /* force statusbar/skin update since we just cleared the whole screen */
77 send_event(GUI_EVENT_ACTIONUPDATE, (void*)1);
78 }
79 else
80 {
81 screens[i].stop_scroll();
82#if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
83 screens[i].backdrop_show(sb_get_backdrop(i));
84#endif
85 viewportmanager_theme_undo(i, skin_has_sbs(i, fms_skin[i].data));
86 }
87 }
88}
89
90
91void fms_skin_init(void)
92{
93 int i;
94 FOR_NB_SCREENS(i)
95 {
96#ifdef HAVE_ALBUMART
97 fms_skin_data[i].albumart = NULL;
98 fms_skin_data[i].playback_aa_slot = -1;
99#endif
100 fms_skin[i].data = &fms_skin_data[i];
101 fms_skin[i].display = &screens[i];
102 /* Currently no seperate wps_state needed/possible
103 so use the only available ( "global" ) one */
104 fms_skin[i].state = &wps_state;
105 fms_skin[i].sync_data = &fms_skin_sync_data;
106 }
107}
108
109int fms_do_button_loop(bool update_screen)
110{
111 return skin_wait_for_action(fms_skin, CONTEXT_FM,
112 update_screen ? TIMEOUT_NOBLOCK : HZ);
113}
114
115struct gui_wps *fms_get(enum screen_type screen)
116{
117 return &fms_skin[screen];
118}
diff --git a/apps/radio/radioart.c b/apps/radio/radioart.c
new file mode 100644
index 0000000000..319f254144
--- /dev/null
+++ b/apps/radio/radioart.c
@@ -0,0 +1,180 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: radio.c -1 $
9 *
10 * Copyright (C) 2010 Jonathan Gordon
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 "radio.h"
28#include "buffering.h"
29#include "file.h"
30#include "kernel.h"
31#include "string-extra.h"
32#include "misc.h"
33
34#define MAX_RADIOART_IMAGES 10
35struct radioart {
36 int handle;
37 long last_tick;
38 struct dim dim;
39 char name[MAX_FMPRESET_LEN+1];
40};
41
42static struct radioart radioart[MAX_RADIOART_IMAGES];
43#ifdef HAVE_RECORDING
44static bool allow_buffer_access = true; /* If we are recording dont touch the buffers! */
45#endif
46static int find_oldest_image(void)
47{
48 int i;
49 long oldest_tick = radioart[0].last_tick;
50 int oldest_idx = 0;
51 for(i=1;i<MAX_RADIOART_IMAGES;i++)
52 {
53 if (radioart[i].last_tick < oldest_tick)
54 {
55 oldest_tick = radioart[i].last_tick;
56 oldest_idx = i;
57 }
58 }
59 return oldest_idx;
60}
61static int load_radioart_image(struct radioart *ra, const char* preset_name,
62 struct dim *dim)
63{
64 char path[MAX_PATH];
65#ifndef HAVE_NOISY_IDLE_MODE
66 cpu_idle_mode(false);
67#endif
68 snprintf(path, sizeof(path), FMPRESET_PATH "/%s.bmp",preset_name);
69 if (!file_exists(path))
70 snprintf(path, sizeof(path), FMPRESET_PATH "/%s.jpg",preset_name);
71 if (!file_exists(path))
72 {
73#ifndef HAVE_NOISY_IDLE_MODE
74 cpu_idle_mode(true);
75#endif
76 return -1;
77 }
78 strlcpy(ra->name, preset_name, MAX_FMPRESET_LEN+1);
79 ra->dim.height = dim->height;
80 ra->dim.width = dim->width;
81 ra->last_tick = current_tick;
82 ra->handle = bufopen(path, 0, TYPE_BITMAP, &ra->dim);
83 if (ra->handle == ERR_BUFFER_FULL)
84 {
85 int i = find_oldest_image();
86 bufclose(i);
87 ra->handle = bufopen(path, 0, TYPE_BITMAP, &ra->dim);
88 }
89#ifndef HAVE_NOISY_IDLE_MODE
90 cpu_idle_mode(true);
91#endif
92 return ra->handle;
93}
94int radio_get_art_hid(struct dim *requested_dim)
95{
96 int preset = radio_current_preset();
97 int i, free_idx = -1;
98 const char* preset_name;
99 if (radio_scan_mode() || preset < 0)
100 return -1;
101#ifdef HAVE_RECORDING
102 if (!allow_buffer_access)
103 return -1;
104#endif
105 preset_name = radio_get_preset_name(preset);
106 for(i=0;i<MAX_RADIOART_IMAGES;i++)
107 {
108 if (radioart[i].handle < 0)
109 {
110 free_idx = i;
111 }
112 else if (!strcmp(radioart[i].name, preset_name) &&
113 radioart[i].dim.width == requested_dim->width &&
114 radioart[i].dim.height == requested_dim->height)
115 {
116 radioart[i].last_tick = current_tick;
117 return radioart[i].handle;
118 }
119 }
120 if (free_idx >= 0)
121 {
122 return load_radioart_image(&radioart[free_idx],
123 preset_name, requested_dim);
124 }
125 else
126 {
127 int i = find_oldest_image();
128 bufclose(radioart[i].handle);
129 return load_radioart_image(&radioart[i],
130 preset_name, requested_dim);
131 }
132
133 return -1;
134}
135static void playback_restarting_handler(void *data)
136{
137 (void)data;
138 int i;
139 for(i=0;i<MAX_RADIOART_IMAGES;i++)
140 {
141 if (radioart[i].handle >= 0)
142 bufclose(radioart[i].handle);
143 radioart[i].handle = -1;
144 radioart[i].name[0] = '\0';
145 }
146}
147#ifdef HAVE_RECORDING
148static void recording_started_handler(void *data)
149{
150 (void)data;
151 allow_buffer_access = false;
152 playback_restarting_handler(NULL);
153}
154static void recording_stopped_handler(void *data)
155{
156 (void)data;
157 allow_buffer_access = true;
158}
159#endif
160
161void radioart_init(bool entering_screen)
162{
163 int i;
164 if (entering_screen)
165 {
166 for(i=0;i<MAX_RADIOART_IMAGES;i++)
167 {
168 radioart[i].handle = -1;
169 radioart[i].name[0] = '\0';
170 }
171 add_event(PLAYBACK_EVENT_START_PLAYBACK, true, playback_restarting_handler);
172 }
173 else
174 {
175#if defined(HAVE_RECORDING)
176 add_event(RECORDING_EVENT_START, false, recording_started_handler);
177 add_event(RECORDING_EVENT_STOP, false, recording_stopped_handler);
178#endif
179 }
180}