summaryrefslogtreecommitdiff
path: root/apps/radio
diff options
context:
space:
mode:
Diffstat (limited to 'apps/radio')
-rw-r--r--apps/radio/presets.c614
-rw-r--r--apps/radio/radio.c1042
-rw-r--r--apps/radio/radio.h84
-rw-r--r--apps/radio/radio_skin.c118
-rw-r--r--apps/radio/radioart.c180
5 files changed, 2038 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}
diff --git a/apps/radio/radio.c b/apps/radio/radio.c
new file mode 100644
index 0000000000..b3540610f1
--- /dev/null
+++ b/apps/radio/radio.c
@@ -0,0 +1,1042 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 "mas.h"
27#include "settings.h"
28#include "button.h"
29#include "status.h"
30#include "thread.h"
31#include "audio.h"
32#include "mp3_playback.h"
33#include "ctype.h"
34#include "file.h"
35#include "general.h"
36#include "errno.h"
37#include "string-extra.h"
38#include "system.h"
39#include "radio.h"
40#include "menu.h"
41#include "misc.h"
42#include "keyboard.h"
43#include "screens.h"
44#include "peakmeter.h"
45#include "lang.h"
46#include "font.h"
47#include "sound_menu.h"
48#ifdef HAVE_RECORDING
49#include "recording.h"
50#endif
51#ifdef IPOD_ACCESSORY_PROTOCOL
52#include "iap.h"
53#endif
54#include "appevents.h"
55#include "talk.h"
56#include "tuner.h"
57#include "power.h"
58#include "sound.h"
59#include "screen_access.h"
60#include "splash.h"
61#include "yesno.h"
62#include "buttonbar.h"
63#include "tree.h"
64#include "dir.h"
65#include "action.h"
66#include "list.h"
67#include "menus/exported_menus.h"
68#include "root_menu.h"
69#include "viewport.h"
70#include "skin_engine/skin_engine.h"
71#include "statusbar-skinned.h"
72#include "buffering.h"
73
74#if CONFIG_TUNER
75
76#if CONFIG_KEYPAD == RECORDER_PAD
77#define FM_RECORD
78#define FM_PRESET_ADD
79#define FM_PRESET_ACTION
80#define FM_PRESET
81#define FM_MODE
82
83#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
84#define FM_PRESET
85#define FM_MODE
86#define FM_NEXT_PRESET
87#define FM_PREV_PRESET
88
89#elif CONFIG_KEYPAD == IRIVER_H10_PAD
90#define FM_PRESET
91#define FM_MODE
92
93#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
94#define FM_PRESET
95#define FM_MODE
96/* This should be removeable if the whole tuning thing is sorted out since
97 proper tuning quiets the screen almost entirely in that extreme measures
98 have to be taken to hear any interference. */
99#define HAVE_NOISY_IDLE_MODE
100
101#elif CONFIG_KEYPAD == ONDIO_PAD
102#define FM_RECORD_DBLPRE
103#define FM_RECORD
104#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || (CONFIG_KEYPAD == SANSA_C200_PAD)
105#define FM_MENU
106#define FM_PRESET
107#define FM_STOP
108#define FM_MODE
109#define FM_EXIT
110#define FM_PLAY
111
112#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
113#define FM_PRESET
114#define FM_MODE
115
116#elif (CONFIG_KEYPAD == COWON_D2_PAD)
117#define FM_MENU
118#define FM_PRESET
119#define FM_STOP
120#define FM_MODE
121#define FM_EXIT
122#define FM_PLAY
123
124#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
125 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
126#define FM_MENU
127#define FM_STOP
128#define FM_EXIT
129#define FM_PLAY
130#define FM_MODE
131
132#endif
133
134/* presets.c needs these so keep unstatic or redo the whole thing! */
135int curr_freq; /* current frequency in Hz */
136/* these are all in presets.c... someone PLEASE rework this ! */
137int handle_radio_presets(void);
138static bool radio_menu(void);
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;
155static int search_dir = 0;
156
157static int radio_status = FMRADIO_OFF;
158static bool in_screen = false;
159
160
161static void radio_off(void);
162
163bool radio_scan_mode(void)
164{
165 return radio_mode == RADIO_SCAN_MODE;
166}
167
168bool radio_is_stereo(void)
169{
170 return tuner_get(RADIO_STEREO) && !global_settings.fm_force_mono;
171}
172int radio_current_frequency(void)
173{
174 return curr_freq;
175}
176
177/* Function to manipulate all yesno dialogues.
178 This function needs the output text as an argument. */
179bool yesno_pop(const char* text)
180{
181 int i;
182 const char *lines[]={text};
183 const struct text_message message={lines, 1};
184 bool ret = (gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
185 FOR_NB_SCREENS(i)
186 screens[i].clear_viewport();
187 return ret;
188}
189
190void radio_init(void)
191{
192 tuner_init();
193 radio_off();
194#ifdef HAVE_ALBUMART
195 radioart_init(false);
196#endif
197}
198
199int get_radio_status(void)
200{
201 return radio_status;
202}
203
204bool in_radio_screen(void)
205{
206 return in_screen;
207}
208
209/* TODO: Move some more of the control functionality to firmware
210 and clean up the mess */
211
212/* secret flag for starting paused - prevents unmute */
213#define FMRADIO_START_PAUSED 0x8000
214void radio_start(void)
215{
216 const struct fm_region_data *fmr;
217 bool start_paused;
218
219 if(radio_status == FMRADIO_PLAYING)
220 return;
221
222 fmr = &fm_region_data[global_settings.fm_region];
223
224 start_paused = radio_status & FMRADIO_START_PAUSED;
225 /* clear flag before any yielding */
226 radio_status &= ~FMRADIO_START_PAUSED;
227
228 if(radio_status == FMRADIO_OFF)
229 tuner_power(true);
230
231 curr_freq = global_status.last_frequency * fmr->freq_step + fmr->freq_min;
232
233 tuner_set(RADIO_SLEEP, 0); /* wake up the tuner */
234
235 if(radio_status == FMRADIO_OFF)
236 {
237#ifdef HAVE_RADIO_REGION
238 tuner_set(RADIO_REGION, global_settings.fm_region);
239#endif
240 tuner_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
241 }
242
243 tuner_set(RADIO_FREQUENCY, curr_freq);
244
245#ifdef HAVE_RADIO_MUTE_TIMEOUT
246 {
247 unsigned long mute_timeout = current_tick + HZ;
248 if (radio_status != FMRADIO_OFF)
249 {
250 /* paused */
251 mute_timeout += HZ;
252 }
253
254 while(!tuner_get(RADIO_STEREO) && !tuner_get(RADIO_TUNED))
255 {
256 if(TIME_AFTER(current_tick, mute_timeout))
257 break;
258 yield();
259 }
260 }
261#endif
262
263 /* keep radio from sounding initially */
264 if(!start_paused)
265 tuner_set(RADIO_MUTE, 0);
266
267 radio_status = FMRADIO_PLAYING;
268} /* radio_start */
269
270void radio_pause(void)
271{
272 if(radio_status == FMRADIO_PAUSED)
273 return;
274
275 if(radio_status == FMRADIO_OFF)
276 {
277 radio_status |= FMRADIO_START_PAUSED;
278 radio_start();
279 }
280
281 tuner_set(RADIO_MUTE, 1);
282 tuner_set(RADIO_SLEEP, 1);
283
284 radio_status = FMRADIO_PAUSED;
285} /* radio_pause */
286
287static void radio_off(void)
288{
289 tuner_set(RADIO_MUTE, 1);
290 tuner_set(RADIO_SLEEP, 1); /* low power mode, if available */
291 radio_status = FMRADIO_OFF;
292 tuner_power(false); /* status update, power off if avail. */
293}
294
295void radio_stop(void)
296{
297 if(radio_status == FMRADIO_OFF)
298 return;
299
300 radio_off();
301} /* radio_stop */
302
303bool radio_hardware_present(void)
304{
305 return tuner_get(RADIO_PRESENT);
306}
307
308/* Keep freq on the grid for the current region */
309int snap_freq_to_grid(int freq)
310{
311 const struct fm_region_data * const fmr =
312 &fm_region_data[global_settings.fm_region];
313
314 /* Range clamp if out of range or just round to nearest */
315 if (freq < fmr->freq_min)
316 freq = fmr->freq_min;
317 else if (freq > fmr->freq_max)
318 freq = fmr->freq_max;
319 else
320 freq = (freq - fmr->freq_min + fmr->freq_step/2) /
321 fmr->freq_step * fmr->freq_step + fmr->freq_min;
322
323 return freq;
324}
325
326void remember_frequency(void)
327{
328 const struct fm_region_data * const fmr =
329 &fm_region_data[global_settings.fm_region];
330 global_status.last_frequency = (curr_freq - fmr->freq_min)
331 / fmr->freq_step;
332 status_save();
333}
334
335/* Step to the next or previous frequency */
336static int step_freq(int freq, int direction)
337{
338 const struct fm_region_data * const fmr =
339 &fm_region_data[global_settings.fm_region];
340
341 freq += direction*fmr->freq_step;
342
343 /* Wrap first or snapping to grid will not let us on the band extremes */
344 if (freq > fmr->freq_max)
345 freq = direction > 0 ? fmr->freq_min : fmr->freq_max;
346 else if (freq < fmr->freq_min)
347 freq = direction < 0 ? fmr->freq_max : fmr->freq_min;
348 else
349 freq = snap_freq_to_grid(freq);
350
351 return freq;
352}
353
354/* Step to the next or previous station */
355void next_station(int direction)
356{
357 if (direction != 0 && radio_mode != RADIO_SCAN_MODE)
358 {
359 next_preset(direction);
360 return;
361 }
362
363 curr_freq = step_freq(curr_freq, direction);
364
365 if (radio_status == FMRADIO_PLAYING)
366 tuner_set(RADIO_MUTE, 1);
367
368 tuner_set(RADIO_FREQUENCY, curr_freq);
369
370 if (radio_status == FMRADIO_PLAYING)
371 tuner_set(RADIO_MUTE, 0);
372
373 set_current_preset(find_preset(curr_freq));
374 remember_frequency();
375}
376
377/* Ends an in-progress search */
378static void end_search(void)
379{
380 if (search_dir != 0 && radio_status == FMRADIO_PLAYING)
381 tuner_set(RADIO_MUTE, 0);
382 search_dir = 0;
383}
384
385/* Speak a frequency. */
386void talk_freq(int freq, bool enqueue)
387{
388 freq /= 10000;
389 talk_number(freq / 100, enqueue);
390 talk_id(LANG_POINT, true);
391 talk_number(freq % 100 / 10, true);
392 if (freq % 10)
393 talk_number(freq % 10, true);
394}
395
396
397int radio_screen(void)
398{
399 bool done = false;
400 int ret_val = GO_TO_ROOT;
401 int button;
402 int i;
403 bool stereo = false, last_stereo = false;
404 bool update_screen = true, restore = true;
405 bool screen_freeze = false;
406 bool keep_playing = false;
407 bool talk = false;
408#ifdef FM_RECORD_DBLPRE
409 int lastbutton = BUTTON_NONE;
410 unsigned long rec_lastclick = 0;
411#endif
412#if CONFIG_CODEC != SWCODEC
413 bool have_recorded = false;
414 int timeout = current_tick + HZ/10;
415 unsigned int last_seconds = 0;
416#ifndef SIMULATOR
417 unsigned int seconds = 0;
418 struct audio_recording_options rec_options;
419#endif
420#endif /* CONFIG_CODEC != SWCODEC */
421#ifndef HAVE_NOISY_IDLE_MODE
422 int button_timeout = current_tick + (2*HZ);
423#endif
424
425 /* change status to "in screen" */
426 in_screen = true;
427
428 if(radio_preset_count() <= 0)
429 {
430 radio_load_presets(global_settings.fmr_file);
431 }
432#ifdef HAVE_ALBUMART
433 radioart_init(true);
434#endif
435
436 if(radio_status == FMRADIO_OFF)
437 audio_stop();
438
439#ifndef SIMULATOR
440
441#if CONFIG_CODEC != SWCODEC
442 if(rec_create_directory() > 0)
443 have_recorded = true;
444
445 audio_init_recording(talk_get_bufsize());
446
447 sound_settings_apply();
448 /* Yes, we use the D/A for monitoring */
449 peak_meter_playback(true);
450
451 peak_meter_enable(true);
452
453 rec_init_recording_options(&rec_options);
454 rec_options.rec_source = AUDIO_SRC_LINEIN;
455 rec_set_recording_options(&rec_options);
456
457 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
458 sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
459
460#endif /* CONFIG_CODEC != SWCODEC */
461#endif /* ndef SIMULATOR */
462
463 /* turn on radio */
464#if CONFIG_CODEC == SWCODEC
465 audio_set_input_source(AUDIO_SRC_FMRADIO,
466 (radio_status == FMRADIO_PAUSED) ?
467 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
468#else
469 if (radio_status == FMRADIO_OFF)
470 radio_start();
471#endif
472
473 if(radio_preset_count() < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN)))
474 scan_presets(NULL);
475
476 set_current_preset(find_preset(curr_freq));
477 if(radio_current_preset() != -1)
478 radio_mode = RADIO_PRESET_MODE;
479
480#ifndef HAVE_NOISY_IDLE_MODE
481 cpu_idle_mode(true);
482#endif
483
484 while(!done)
485 {
486 if(search_dir != 0)
487 {
488 curr_freq = step_freq(curr_freq, search_dir);
489 update_screen = true;
490
491 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
492 {
493 set_current_preset(find_preset(curr_freq));
494 remember_frequency();
495 end_search();
496 talk = true;
497 }
498 trigger_cpu_boost();
499 }
500
501 if (!update_screen)
502 {
503 cancel_cpu_boost();
504 }
505
506 button = fms_do_button_loop(update_screen);
507
508#ifndef HAVE_NOISY_IDLE_MODE
509 if (button != ACTION_NONE)
510 {
511 cpu_idle_mode(false);
512 button_timeout = current_tick + (2*HZ);
513 }
514#endif
515 switch(button)
516 {
517 case ACTION_FM_STOP:
518#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
519 if(audio_status() == AUDIO_STATUS_RECORD)
520 {
521 audio_stop();
522 }
523 else
524#endif
525 {
526 done = true;
527 if(has_presets_changed())
528 {
529 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
530 {
531 presets_save();
532 }
533 }
534 }
535 update_screen = true;
536 break;
537
538#ifdef FM_RECORD
539 case ACTION_FM_RECORD:
540#ifdef FM_RECORD_DBLPRE
541 if (lastbutton != ACTION_FM_RECORD_DBLPRE)
542 {
543 rec_lastclick = 0;
544 break;
545 }
546 if (current_tick - rec_lastclick > HZ/2)
547 {
548 rec_lastclick = current_tick;
549 break;
550 }
551#endif /* FM_RECORD_DBLPRE */
552#ifndef SIMULATOR
553 if(audio_status() == AUDIO_STATUS_RECORD)
554 {
555 rec_command(RECORDING_CMD_START_NEWFILE);
556 update_screen = true;
557 }
558 else
559 {
560 have_recorded = true;
561 rec_command(RECORDING_CMD_START);
562 update_screen = true;
563 }
564#endif /* SIMULATOR */
565 last_seconds = 0;
566 break;
567#endif /* #ifdef FM_RECORD */
568
569 case ACTION_FM_EXIT:
570#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
571 if(audio_status() == AUDIO_STATUS_RECORD)
572 audio_stop();
573#endif
574 keep_playing = true;
575 done = true;
576 ret_val = GO_TO_ROOT;
577 if(has_presets_changed())
578 {
579 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
580 {
581 presets_save();
582 }
583 }
584
585 break;
586
587 case ACTION_STD_PREV:
588 case ACTION_STD_NEXT:
589 next_station(button == ACTION_STD_PREV ? -1 : 1);
590 end_search();
591 update_screen = true;
592 talk = true;
593 break;
594
595 case ACTION_STD_PREVREPEAT:
596 case ACTION_STD_NEXTREPEAT:
597 {
598 int dir = search_dir;
599 search_dir = button == ACTION_STD_PREVREPEAT ? -1 : 1;
600 if (radio_mode != RADIO_SCAN_MODE)
601 {
602 next_preset(search_dir);
603 end_search();
604 update_screen = true;
605 talk = true;
606 }
607 else if (dir == 0)
608 {
609 /* Starting auto scan */
610 tuner_set(RADIO_MUTE, 1);
611 update_screen = true;
612 }
613 break;
614 }
615
616 case ACTION_SETTINGS_INC:
617 case ACTION_SETTINGS_INCREPEAT:
618 global_settings.volume++;
619 setvol();
620 update_screen = true;
621 break;
622
623 case ACTION_SETTINGS_DEC:
624 case ACTION_SETTINGS_DECREPEAT:
625 global_settings.volume--;
626 setvol();
627 update_screen = true;
628 break;
629
630 case ACTION_FM_PLAY:
631 if (radio_status == FMRADIO_PLAYING)
632 radio_pause();
633 else
634 radio_start();
635
636 update_screen = true;
637 talk = false;
638 talk_shutup();
639 break;
640
641 case ACTION_FM_MENU:
642 fms_fix_displays(FMS_EXIT);
643 radio_menu();
644 set_current_preset(find_preset(curr_freq));
645 update_screen = true;
646 restore = true;
647 break;
648
649#ifdef FM_PRESET
650 case ACTION_FM_PRESET:
651 if(radio_preset_count() < 1)
652 {
653 splash(HZ, ID2P(LANG_FM_NO_PRESETS));
654 update_screen = true;
655 break;
656 }
657 fms_fix_displays(FMS_EXIT);
658 handle_radio_presets();
659 update_screen = true;
660 restore = true;
661 break;
662#endif /* FM_PRESET */
663
664#ifdef FM_FREEZE
665 case ACTION_FM_FREEZE:
666 if(!screen_freeze)
667 {
668 splash(HZ, str(LANG_FM_FREEZE));
669 screen_freeze = true;
670 }
671 else
672 {
673 update_screen = true;
674 screen_freeze = false;
675 }
676 break;
677#endif /* FM_FREEZE */
678
679 case SYS_USB_CONNECTED:
680#if CONFIG_CODEC != SWCODEC
681 /* Only accept USB connection when not recording */
682 if(audio_status() != AUDIO_STATUS_RECORD)
683#endif
684 {
685 default_event_handler(SYS_USB_CONNECTED);
686 screen_freeze = true; /* Cosmetic: makes sure the
687 radio screen doesn't redraw */
688 done = true;
689 }
690 break;
691
692#ifdef FM_MODE
693 case ACTION_FM_MODE:
694 if(radio_mode == RADIO_SCAN_MODE)
695 {
696 /* Force scan mode if there are no presets. */
697 if(radio_preset_count() > 0)
698 radio_mode = RADIO_PRESET_MODE;
699 }
700 else
701 radio_mode = RADIO_SCAN_MODE;
702 update_screen = true;
703 cond_talk_ids_fq(radio_mode ?
704 LANG_PRESET : LANG_RADIO_SCAN_MODE);
705 talk = true;
706 break;
707#endif /* FM_MODE */
708
709#ifdef FM_NEXT_PRESET
710 case ACTION_FM_NEXT_PRESET:
711 next_preset(1);
712 end_search();
713 update_screen = true;
714 talk = true;
715 break;
716#endif
717
718#ifdef FM_PREV_PRESET
719 case ACTION_FM_PREV_PRESET:
720 next_preset(-1);
721 end_search();
722 update_screen = true;
723 talk = true;
724 break;
725#endif
726
727 default:
728 default_event_handler(button);
729#ifdef HAVE_RDS_CAP
730 if (tuner_get(RADIO_EVENT))
731 update_screen = true;
732#endif
733 if (!tuner_get(RADIO_PRESENT))
734 {
735#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
736 if(audio_status() == AUDIO_STATUS_RECORD)
737 audio_stop();
738#endif
739 keep_playing = false;
740 done = true;
741 ret_val = GO_TO_ROOT;
742 if(has_presets_changed())
743 {
744 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
745 {
746 radio_save_presets();
747 }
748 }
749
750 /* Clear the preset list on exit. */
751 clear_preset_list();
752 }
753 break;
754 } /*switch(button)*/
755
756#ifdef FM_RECORD_DBLPRE
757 if (button != ACTION_NONE)
758 lastbutton = button;
759#endif
760
761#if CONFIG_CODEC != SWCODEC
762 peak_meter_peek();
763#endif
764
765 if(!screen_freeze)
766 {
767 /* Only display the peak meter when not recording */
768#if CONFIG_CODEC != SWCODEC
769 if(TIME_AFTER(current_tick, timeout))
770 {
771 timeout = current_tick + HZ;
772#else /* SWCODEC */
773 {
774#endif /* CONFIG_CODEC == SWCODEC */
775
776 /* keep "mono" from always being displayed when paused */
777 if (radio_status != FMRADIO_PAUSED)
778 {
779 stereo = tuner_get(RADIO_STEREO) &&
780 !global_settings.fm_force_mono;
781
782 if(stereo != last_stereo)
783 {
784 update_screen = true;
785 last_stereo = stereo;
786 }
787 }
788 }
789
790#if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
791 seconds = audio_recorded_time() / HZ;
792 if (update_screen || seconds > last_seconds || restore)
793 {
794 last_seconds = seconds;
795#else
796 if (update_screen || restore)
797 {
798#endif
799 if (restore)
800 fms_fix_displays(FMS_ENTER);
801 FOR_NB_SCREENS(i)
802 skin_update(fms_get(i), WPS_REFRESH_ALL);
803 restore = false;
804 }
805 }
806 update_screen = false;
807
808 if (global_settings.talk_file && talk
809 && radio_status == FMRADIO_PAUSED)
810 {
811 talk = false;
812 bool enqueue = false;
813 if (radio_mode == RADIO_SCAN_MODE)
814 {
815 talk_freq(curr_freq, enqueue);
816 enqueue = true;
817 }
818 if (radio_current_preset() >= 0)
819 talk_preset(radio_current_preset(), radio_mode == RADIO_PRESET_MODE,
820 enqueue);
821 }
822
823#if CONFIG_CODEC != SWCODEC
824 if(audio_status() & AUDIO_STATUS_ERROR)
825 {
826 done = true;
827 }
828#endif
829
830#ifndef HAVE_NOISY_IDLE_MODE
831 if (TIME_AFTER(current_tick, button_timeout))
832 {
833 cpu_idle_mode(true);
834 }
835#endif
836 } /*while(!done)*/
837
838#ifndef SIMULATOR
839#if CONFIG_CODEC != SWCODEC
840 if(audio_status() & AUDIO_STATUS_ERROR)
841 {
842 splash(0, str(LANG_DISK_FULL));
843 audio_error_clear();
844
845 while(1)
846 {
847 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
848 if(button == ACTION_FM_STOP)
849 break;
850 }
851 }
852
853 audio_init_playback();
854#endif /* CONFIG_CODEC != SWCODEC */
855
856 sound_settings_apply();
857#endif /* SIMULATOR */
858
859 if(keep_playing)
860 {
861/* Catch FMRADIO_PLAYING status for the sim. */
862#ifndef SIMULATOR
863#if CONFIG_CODEC != SWCODEC
864 /* Enable the Left and right A/D Converter */
865 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
866 sound_default(SOUND_RIGHT_GAIN),
867 AUDIO_GAIN_LINEIN);
868 mas_codec_writereg(6, 0x4000);
869#endif
870 end_search();
871#endif /* SIMULATOR */
872 }
873 else
874 {
875#if CONFIG_CODEC == SWCODEC
876 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
877#else
878 radio_stop();
879#endif
880 }
881
882#ifndef HAVE_NOISY_IDLE_MODE
883 cpu_idle_mode(false);
884#endif
885 fms_fix_displays(FMS_EXIT);
886 in_screen = false;
887#if CONFIG_CODEC != SWCODEC
888 return have_recorded;
889#else
890 return false;
891#endif
892} /* radio_screen */
893
894void toggle_mono_mode(bool mono)
895{
896 tuner_set(RADIO_FORCE_MONO, mono);
897}
898
899void set_radio_region(int region)
900{
901#ifdef HAVE_RADIO_REGION
902 tuner_set(RADIO_REGION, region);
903#endif
904 next_station(0);
905 remember_frequency();
906 (void)region;
907}
908
909MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
910MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
911
912#ifndef FM_MODE
913static char* get_mode_text(int selected_item, void * data, char *buffer)
914{
915 (void)selected_item;
916 (void)data;
917 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_MODE),
918 radio_mode ? str(LANG_PRESET) :
919 str(LANG_RADIO_SCAN_MODE));
920 return buffer;
921}
922static int toggle_radio_mode(void)
923{
924 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
925 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
926 return 0;
927}
928MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
929 toggle_radio_mode, NULL,
930 get_mode_text, NULL, NULL, NULL, Icon_NOICON);
931#endif
932
933
934
935#ifdef HAVE_RECORDING
936
937#if defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC
938#define FM_RECORDING_SCREEN
939static int fm_recording_screen(void)
940{
941 bool ret;
942
943 /* switch recording source to FMRADIO for the duration */
944 int rec_source = global_settings.rec_source;
945 global_settings.rec_source = AUDIO_SRC_FMRADIO;
946 ret = recording_screen(true);
947
948 /* safe to reset as changing sources is prohibited here */
949 global_settings.rec_source = rec_source;
950
951 return ret;
952}
953
954#endif /* defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC */
955
956#if defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC
957#define FM_RECORDING_SETTINGS
958static int fm_recording_settings(void)
959{
960 bool ret = recording_menu(true);
961
962#if CONFIG_CODEC != SWCODEC
963 if (!ret)
964 {
965 struct audio_recording_options rec_options;
966 rec_init_recording_options(&rec_options);
967 rec_options.rec_source = AUDIO_SRC_LINEIN;
968 rec_set_recording_options(&rec_options);
969 }
970#endif
971
972 return ret;
973}
974
975#endif /* defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC */
976#endif /* HAVE_RECORDING */
977
978#ifdef FM_RECORDING_SCREEN
979MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING),
980 fm_recording_screen, NULL, NULL, Icon_Recording);
981#endif
982#ifdef FM_RECORDING_SETTINGS
983MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
984 fm_recording_settings, NULL, NULL, Icon_Recording);
985#endif
986#ifndef FM_PRESET
987int handle_radio_presets_menu(void)
988{
989 return handle_radio_presets();
990}
991MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
992 handle_radio_presets_menu, NULL, NULL, Icon_NOICON);
993#endif
994#ifndef FM_PRESET_ADD
995int handle_radio_addpreset_menu(void)
996{
997 return radio_add_preset();
998}
999MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1000 radio_add_preset, NULL, NULL, Icon_NOICON);
1001#endif
1002
1003
1004MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1005 load_preset_list, NULL, NULL, Icon_NOICON);
1006MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1007 save_preset_list, NULL, NULL, Icon_NOICON);
1008MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1009 clear_preset_list, NULL, NULL, Icon_NOICON);
1010MENUITEM_FUNCTION(scan_presets_item, MENU_FUNC_USEPARAM,
1011 ID2P(LANG_FM_SCAN_PRESETS),
1012 scan_presets, NULL, NULL, Icon_NOICON);
1013
1014MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
1015 Icon_Radio_screen,
1016#ifndef FM_PRESET
1017 &radio_presets_item,
1018#endif
1019#ifndef FM_PRESET_ADD
1020 &radio_addpreset_item,
1021#endif
1022 &presetload_item, &presetsave_item, &presetclear_item,
1023 &force_mono,
1024#ifndef FM_MODE
1025 &radio_mode_item,
1026#endif
1027 &set_region, &sound_settings,
1028#ifdef FM_RECORDING_SCREEN
1029 &recscreen_item,
1030#endif
1031#ifdef FM_RECORDING_SETTINGS
1032 &recsettings_item,
1033#endif
1034 &scan_presets_item);
1035/* main menu of the radio screen */
1036static bool radio_menu(void)
1037{
1038 return do_menu(&radio_settings_menu, NULL, NULL, false) ==
1039 MENU_ATTACHED_USB;
1040}
1041
1042#endif
diff --git a/apps/radio/radio.h b/apps/radio/radio.h
new file mode 100644
index 0000000000..f7b0a0e80b
--- /dev/null
+++ b/apps/radio/radio.h
@@ -0,0 +1,84 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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#ifndef RADIO_H
22#define RADIO_H
23
24#ifndef FMRADIO_H
25#include "fmradio.h"
26#endif
27#include "screen_access.h"
28#include "bmp.h"
29
30enum {
31 RADIO_SCAN_MODE = 0,
32 RADIO_PRESET_MODE,
33};
34
35#if CONFIG_TUNER
36void radio_load_presets(char *filename);
37void radio_init(void) INIT_ATTR;
38int radio_screen(void);
39void radio_start(void);
40void radio_pause(void);
41void radio_stop(void);
42bool radio_hardware_present(void);
43bool in_radio_screen(void);
44
45bool radio_scan_mode(void); /* true for scan mode, false for preset mode */
46bool radio_is_stereo(void);
47int radio_current_frequency(void);
48int radio_current_preset(void);
49int radio_preset_count(void);
50const struct fmstation *radio_get_preset(int preset);
51
52/* skin functions */
53void fms_data_load(enum screen_type screen, const char *buf, bool isfile);
54void fms_skin_init(void);
55
56/* callbacks for the radio settings */
57void set_radio_region(int region);
58void toggle_mono_mode(bool mono);
59
60#define MAX_FMPRESET_LEN 27
61
62struct fmstation
63{
64 int frequency; /* In Hz */
65 char name[MAX_FMPRESET_LEN+1];
66};
67const char* radio_get_preset_name(int preset);
68
69#ifdef HAVE_ALBUMART
70void radioart_init(bool entering_screen);
71int radio_get_art_hid(struct dim *requested_dim);
72#endif
73
74void next_station(int direction);
75
76
77enum fms_exiting {
78 FMS_EXIT,
79 FMS_ENTER
80};
81
82#endif /* CONFIG_TUNER */
83
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}