diff options
Diffstat (limited to 'apps/radio')
-rw-r--r-- | apps/radio/presets.c | 614 | ||||
-rw-r--r-- | apps/radio/radio.c | 1042 | ||||
-rw-r--r-- | apps/radio/radio.h | 84 | ||||
-rw-r--r-- | apps/radio/radio_skin.c | 118 | ||||
-rw-r--r-- | apps/radio/radioart.c | 180 |
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 | |||
44 | static int curr_preset = -1; | ||
45 | |||
46 | extern int curr_freq; /* from radio.c.. naughty but meh */ | ||
47 | extern int radio_mode; | ||
48 | int snap_freq_to_grid(int freq); | ||
49 | void remember_frequency(void); | ||
50 | void talk_freq(int freq, bool enqueue); | ||
51 | |||
52 | #define MAX_PRESETS 64 | ||
53 | static bool presets_loaded = false, presets_changed = false; | ||
54 | static struct fmstation presets[MAX_PRESETS]; | ||
55 | |||
56 | static char filepreset[MAX_PATH]; /* preset filename variable */ | ||
57 | |||
58 | static int num_presets = 0; /* The number of presets in the preset list */ | ||
59 | |||
60 | bool yesno_pop(const char* text); /* radio.c */ | ||
61 | |||
62 | int radio_current_preset(void) | ||
63 | { | ||
64 | return curr_preset; | ||
65 | } | ||
66 | int radio_preset_count(void) | ||
67 | { | ||
68 | return num_presets; | ||
69 | } | ||
70 | const struct fmstation *radio_get_preset(int preset) | ||
71 | { | ||
72 | return &presets[preset]; | ||
73 | } | ||
74 | |||
75 | bool has_presets_changed(void) | ||
76 | { | ||
77 | return presets_changed; | ||
78 | } | ||
79 | |||
80 | |||
81 | /* Find a matching preset to freq */ | ||
82 | int 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. */ | ||
98 | int 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 | |||
146 | void 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 | |||
163 | void 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. */ | ||
169 | void 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 | |||
183 | void 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 | |||
207 | void 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 | |||
266 | const char* radio_get_preset_name(int preset) | ||
267 | { | ||
268 | if (preset < num_presets) | ||
269 | return presets[preset].name; | ||
270 | return NULL; | ||
271 | } | ||
272 | |||
273 | int 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 */ | ||
300 | static int selected_preset = -1; | ||
301 | static 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 | |||
321 | static 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 | |||
351 | int load_preset_list(void) | ||
352 | { | ||
353 | return !rockbox_browse(FMPRESET_PATH, SHOW_FMR); | ||
354 | } | ||
355 | |||
356 | int 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 | |||
409 | int 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 | |||
424 | MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL, | ||
425 | ID2P(LANG_FM_EDIT_PRESET), | ||
426 | radio_edit_preset, NULL, NULL, Icon_NOICON); | ||
427 | MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL, | ||
428 | ID2P(LANG_FM_DELETE_PRESET), | ||
429 | radio_delete_preset, NULL, NULL, Icon_NOICON); | ||
430 | static 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 | } | ||
438 | MAKE_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 */ | ||
442 | static 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 | |||
457 | static 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 | |||
464 | int 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 | |||
534 | int 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 | |||
608 | void 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! */ | ||
135 | int curr_freq; /* current frequency in Hz */ | ||
136 | /* these are all in presets.c... someone PLEASE rework this ! */ | ||
137 | int handle_radio_presets(void); | ||
138 | static bool radio_menu(void); | ||
139 | int radio_add_preset(void); | ||
140 | int save_preset_list(void); | ||
141 | int load_preset_list(void); | ||
142 | int clear_preset_list(void); | ||
143 | void next_preset(int direction); | ||
144 | void set_current_preset(int preset); | ||
145 | int scan_presets(void *viewports); | ||
146 | int find_preset(int freq); | ||
147 | void radio_save_presets(void); | ||
148 | bool has_presets_changed(void); | ||
149 | void talk_preset(int preset, bool fallback, bool enqueue); | ||
150 | void presets_save(void); | ||
151 | |||
152 | |||
153 | |||
154 | int radio_mode = RADIO_SCAN_MODE; | ||
155 | static int search_dir = 0; | ||
156 | |||
157 | static int radio_status = FMRADIO_OFF; | ||
158 | static bool in_screen = false; | ||
159 | |||
160 | |||
161 | static void radio_off(void); | ||
162 | |||
163 | bool radio_scan_mode(void) | ||
164 | { | ||
165 | return radio_mode == RADIO_SCAN_MODE; | ||
166 | } | ||
167 | |||
168 | bool radio_is_stereo(void) | ||
169 | { | ||
170 | return tuner_get(RADIO_STEREO) && !global_settings.fm_force_mono; | ||
171 | } | ||
172 | int 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. */ | ||
179 | bool 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 | |||
190 | void radio_init(void) | ||
191 | { | ||
192 | tuner_init(); | ||
193 | radio_off(); | ||
194 | #ifdef HAVE_ALBUMART | ||
195 | radioart_init(false); | ||
196 | #endif | ||
197 | } | ||
198 | |||
199 | int get_radio_status(void) | ||
200 | { | ||
201 | return radio_status; | ||
202 | } | ||
203 | |||
204 | bool 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 | ||
214 | void 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 | |||
270 | void 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 | |||
287 | static 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 | |||
295 | void radio_stop(void) | ||
296 | { | ||
297 | if(radio_status == FMRADIO_OFF) | ||
298 | return; | ||
299 | |||
300 | radio_off(); | ||
301 | } /* radio_stop */ | ||
302 | |||
303 | bool radio_hardware_present(void) | ||
304 | { | ||
305 | return tuner_get(RADIO_PRESENT); | ||
306 | } | ||
307 | |||
308 | /* Keep freq on the grid for the current region */ | ||
309 | int 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 | |||
326 | void 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 */ | ||
336 | static 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 */ | ||
355 | void 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 */ | ||
378 | static 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. */ | ||
386 | void 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 | |||
397 | int 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 | |||
894 | void toggle_mono_mode(bool mono) | ||
895 | { | ||
896 | tuner_set(RADIO_FORCE_MONO, mono); | ||
897 | } | ||
898 | |||
899 | void 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 | |||
909 | MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL); | ||
910 | MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL); | ||
911 | |||
912 | #ifndef FM_MODE | ||
913 | static 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 | } | ||
922 | static 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 | } | ||
928 | MENUITEM_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 | ||
939 | static 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 | ||
958 | static 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 | ||
979 | MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING), | ||
980 | fm_recording_screen, NULL, NULL, Icon_Recording); | ||
981 | #endif | ||
982 | #ifdef FM_RECORDING_SETTINGS | ||
983 | MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS), | ||
984 | fm_recording_settings, NULL, NULL, Icon_Recording); | ||
985 | #endif | ||
986 | #ifndef FM_PRESET | ||
987 | int handle_radio_presets_menu(void) | ||
988 | { | ||
989 | return handle_radio_presets(); | ||
990 | } | ||
991 | MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET), | ||
992 | handle_radio_presets_menu, NULL, NULL, Icon_NOICON); | ||
993 | #endif | ||
994 | #ifndef FM_PRESET_ADD | ||
995 | int handle_radio_addpreset_menu(void) | ||
996 | { | ||
997 | return radio_add_preset(); | ||
998 | } | ||
999 | MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET), | ||
1000 | radio_add_preset, NULL, NULL, Icon_NOICON); | ||
1001 | #endif | ||
1002 | |||
1003 | |||
1004 | MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD), | ||
1005 | load_preset_list, NULL, NULL, Icon_NOICON); | ||
1006 | MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE), | ||
1007 | save_preset_list, NULL, NULL, Icon_NOICON); | ||
1008 | MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR), | ||
1009 | clear_preset_list, NULL, NULL, Icon_NOICON); | ||
1010 | MENUITEM_FUNCTION(scan_presets_item, MENU_FUNC_USEPARAM, | ||
1011 | ID2P(LANG_FM_SCAN_PRESETS), | ||
1012 | scan_presets, NULL, NULL, Icon_NOICON); | ||
1013 | |||
1014 | MAKE_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 */ | ||
1036 | static 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 | |||
30 | enum { | ||
31 | RADIO_SCAN_MODE = 0, | ||
32 | RADIO_PRESET_MODE, | ||
33 | }; | ||
34 | |||
35 | #if CONFIG_TUNER | ||
36 | void radio_load_presets(char *filename); | ||
37 | void radio_init(void) INIT_ATTR; | ||
38 | int radio_screen(void); | ||
39 | void radio_start(void); | ||
40 | void radio_pause(void); | ||
41 | void radio_stop(void); | ||
42 | bool radio_hardware_present(void); | ||
43 | bool in_radio_screen(void); | ||
44 | |||
45 | bool radio_scan_mode(void); /* true for scan mode, false for preset mode */ | ||
46 | bool radio_is_stereo(void); | ||
47 | int radio_current_frequency(void); | ||
48 | int radio_current_preset(void); | ||
49 | int radio_preset_count(void); | ||
50 | const struct fmstation *radio_get_preset(int preset); | ||
51 | |||
52 | /* skin functions */ | ||
53 | void fms_data_load(enum screen_type screen, const char *buf, bool isfile); | ||
54 | void fms_skin_init(void); | ||
55 | |||
56 | /* callbacks for the radio settings */ | ||
57 | void set_radio_region(int region); | ||
58 | void toggle_mono_mode(bool mono); | ||
59 | |||
60 | #define MAX_FMPRESET_LEN 27 | ||
61 | |||
62 | struct fmstation | ||
63 | { | ||
64 | int frequency; /* In Hz */ | ||
65 | char name[MAX_FMPRESET_LEN+1]; | ||
66 | }; | ||
67 | const char* radio_get_preset_name(int preset); | ||
68 | |||
69 | #ifdef HAVE_ALBUMART | ||
70 | void radioart_init(bool entering_screen); | ||
71 | int radio_get_art_hid(struct dim *requested_dim); | ||
72 | #endif | ||
73 | |||
74 | void next_station(int direction); | ||
75 | |||
76 | |||
77 | enum 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 | |||
35 | extern struct wps_state wps_state; /* from wps.c */ | ||
36 | static struct gui_wps fms_skin[NB_SCREENS] = {{ .data = NULL }}; | ||
37 | static struct wps_data fms_skin_data[NB_SCREENS] = {{ .wps_loaded = 0 }}; | ||
38 | static struct wps_sync_data fms_skin_sync_data = { .do_full_update = false }; | ||
39 | |||
40 | void 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 | } | ||
64 | void 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 | |||
91 | void 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 | |||
109 | int 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 | |||
115 | struct 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 | ||
35 | struct radioart { | ||
36 | int handle; | ||
37 | long last_tick; | ||
38 | struct dim dim; | ||
39 | char name[MAX_FMPRESET_LEN+1]; | ||
40 | }; | ||
41 | |||
42 | static struct radioart radioart[MAX_RADIOART_IMAGES]; | ||
43 | #ifdef HAVE_RECORDING | ||
44 | static bool allow_buffer_access = true; /* If we are recording dont touch the buffers! */ | ||
45 | #endif | ||
46 | static 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 | } | ||
61 | static 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 | } | ||
94 | int 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 | } | ||
135 | static 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 | ||
148 | static void recording_started_handler(void *data) | ||
149 | { | ||
150 | (void)data; | ||
151 | allow_buffer_access = false; | ||
152 | playback_restarting_handler(NULL); | ||
153 | } | ||
154 | static void recording_stopped_handler(void *data) | ||
155 | { | ||
156 | (void)data; | ||
157 | allow_buffer_access = true; | ||
158 | } | ||
159 | #endif | ||
160 | |||
161 | void 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 | } | ||