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