fix FS#11264 - frequency bar and presets not working in the sbs
[kugel-rb.git] / apps / recorder / radio.c
blob026579516bd67f8f506f7e4aa001b4ab007071b6
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2003 Linus Nielsen Feltzing
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
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"
73 #if CONFIG_TUNER
75 #if CONFIG_KEYPAD == RECORDER_PAD
76 #define FM_RECORD
77 #define FM_PRESET_ADD
78 #define FM_PRESET_ACTION
79 #define FM_PRESET
80 #define FM_MODE
82 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
83 #define FM_PRESET
84 #define FM_MODE
85 #define FM_NEXT_PRESET
86 #define FM_PREV_PRESET
88 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
89 #define FM_PRESET
90 #define FM_MODE
92 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
93 #define FM_PRESET
94 #define FM_MODE
95 /* This should be removeable if the whole tuning thing is sorted out since
96 proper tuning quiets the screen almost entirely in that extreme measures
97 have to be taken to hear any interference. */
98 #define HAVE_NOISY_IDLE_MODE
100 #elif CONFIG_KEYPAD == ONDIO_PAD
101 #define FM_RECORD_DBLPRE
102 #define FM_RECORD
103 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || (CONFIG_KEYPAD == SANSA_C200_PAD)
104 #define FM_MENU
105 #define FM_PRESET
106 #define FM_STOP
107 #define FM_MODE
108 #define FM_EXIT
109 #define FM_PLAY
111 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
112 #define FM_PRESET
113 #define FM_MODE
115 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
116 #define FM_MENU
117 #define FM_PRESET
118 #define FM_STOP
119 #define FM_MODE
120 #define FM_EXIT
121 #define FM_PLAY
123 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
124 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
125 #define FM_MENU
126 #define FM_STOP
127 #define FM_EXIT
128 #define FM_PLAY
129 #define FM_MODE
131 #endif
133 #define RADIO_SCAN_MODE 0
134 #define RADIO_PRESET_MODE 1
136 static int curr_preset = -1;
137 static int curr_freq; /* current frequency in Hz */
138 static int radio_mode = RADIO_SCAN_MODE;
139 static int search_dir = 0;
141 static int radio_status = FMRADIO_OFF;
142 static bool in_screen = false;
144 #define MAX_PRESETS 64
145 static bool presets_loaded = false, presets_changed = false;
146 static struct fmstation presets[MAX_PRESETS];
148 static char filepreset[MAX_PATH]; /* preset filename variable */
150 static int num_presets = 0; /* The number of presets in the preset list */
152 static void radio_save_presets(void);
153 static int handle_radio_presets(void);
154 static bool radio_menu(void);
155 static int radio_add_preset(void);
156 static int save_preset_list(void);
157 static int load_preset_list(void);
158 static int clear_preset_list(void);
160 static int scan_presets(void *viewports);
161 static void radio_off(void);
163 bool radio_scan_mode(void)
165 return radio_mode == RADIO_SCAN_MODE;
168 bool radio_is_stereo(void)
170 return tuner_get(RADIO_STEREO) && !global_settings.fm_force_mono;
172 int radio_current_frequency(void)
174 return curr_freq;
177 int radio_current_preset(void)
179 return curr_preset;
181 int radio_preset_count(void)
183 return num_presets;
185 const struct fmstation *radio_get_preset(int preset)
187 return &presets[preset];
189 /* Function to manipulate all yesno dialogues.
190 This function needs the output text as an argument. */
191 static bool yesno_pop(const char* text)
193 int i;
194 const char *lines[]={text};
195 const struct text_message message={lines, 1};
196 bool ret = (gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
197 FOR_NB_SCREENS(i)
198 screens[i].clear_viewport();
199 return ret;
202 void radio_init(void)
204 tuner_init();
205 radio_off();
208 int get_radio_status(void)
210 return radio_status;
213 bool in_radio_screen(void)
215 return in_screen;
218 /* TODO: Move some more of the control functionality to firmware
219 and clean up the mess */
221 /* secret flag for starting paused - prevents unmute */
222 #define FMRADIO_START_PAUSED 0x8000
223 void radio_start(void)
225 const struct fm_region_data *fmr;
226 bool start_paused;
228 if(radio_status == FMRADIO_PLAYING)
229 return;
231 fmr = &fm_region_data[global_settings.fm_region];
233 start_paused = radio_status & FMRADIO_START_PAUSED;
234 /* clear flag before any yielding */
235 radio_status &= ~FMRADIO_START_PAUSED;
237 if(radio_status == FMRADIO_OFF)
238 tuner_power(true);
240 curr_freq = global_status.last_frequency * fmr->freq_step + fmr->freq_min;
242 tuner_set(RADIO_SLEEP, 0); /* wake up the tuner */
244 if(radio_status == FMRADIO_OFF)
246 #ifdef HAVE_RADIO_REGION
247 tuner_set(RADIO_REGION, global_settings.fm_region);
248 #endif
249 tuner_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
252 tuner_set(RADIO_FREQUENCY, curr_freq);
254 #ifdef HAVE_RADIO_MUTE_TIMEOUT
256 unsigned long mute_timeout = current_tick + HZ;
257 if (radio_status != FMRADIO_OFF)
259 /* paused */
260 mute_timeout += HZ;
263 while(!tuner_get(RADIO_STEREO) && !tuner_get(RADIO_TUNED))
265 if(TIME_AFTER(current_tick, mute_timeout))
266 break;
267 yield();
270 #endif
272 /* keep radio from sounding initially */
273 if(!start_paused)
274 tuner_set(RADIO_MUTE, 0);
276 radio_status = FMRADIO_PLAYING;
277 } /* radio_start */
279 void radio_pause(void)
281 if(radio_status == FMRADIO_PAUSED)
282 return;
284 if(radio_status == FMRADIO_OFF)
286 radio_status |= FMRADIO_START_PAUSED;
287 radio_start();
290 tuner_set(RADIO_MUTE, 1);
291 tuner_set(RADIO_SLEEP, 1);
293 radio_status = FMRADIO_PAUSED;
294 } /* radio_pause */
296 static void radio_off(void)
298 tuner_set(RADIO_MUTE, 1);
299 tuner_set(RADIO_SLEEP, 1); /* low power mode, if available */
300 radio_status = FMRADIO_OFF;
301 tuner_power(false); /* status update, power off if avail. */
304 void radio_stop(void)
306 if(radio_status == FMRADIO_OFF)
307 return;
309 radio_off();
310 } /* radio_stop */
312 bool radio_hardware_present(void)
314 return tuner_get(RADIO_PRESENT);
317 /* Keep freq on the grid for the current region */
318 static int snap_freq_to_grid(int freq)
320 const struct fm_region_data * const fmr =
321 &fm_region_data[global_settings.fm_region];
323 /* Range clamp if out of range or just round to nearest */
324 if (freq < fmr->freq_min)
325 freq = fmr->freq_min;
326 else if (freq > fmr->freq_max)
327 freq = fmr->freq_max;
328 else
329 freq = (freq - fmr->freq_min + fmr->freq_step/2) /
330 fmr->freq_step * fmr->freq_step + fmr->freq_min;
332 return freq;
335 /* Find a matching preset to freq */
336 static int find_preset(int freq)
338 int i;
339 if(num_presets < 1)
340 return -1;
341 for(i = 0;i < MAX_PRESETS;i++)
343 if(freq == presets[i].frequency)
344 return i;
347 return -1;
350 /* Return the closest preset encountered in the search direction with
351 wraparound. */
352 static int find_closest_preset(int freq, int direction)
354 int i;
355 int lowpreset = 0;
356 int highpreset = 0;
357 int closest = -1;
359 if (direction == 0) /* direction == 0 isn't really used */
360 return 0;
362 for (i = 0; i < num_presets; i++)
364 int f = presets[i].frequency;
365 if (f == freq)
366 return i; /* Exact match = stop */
368 /* remember the highest and lowest presets for wraparound */
369 if (f < presets[lowpreset].frequency)
370 lowpreset = i;
371 if (f > presets[highpreset].frequency)
372 highpreset = i;
374 /* find the closest preset in the given direction */
375 if (direction > 0 && f > freq)
377 if (closest < 0 || f < presets[closest].frequency)
378 closest = i;
380 else if (direction < 0 && f < freq)
382 if (closest < 0 || f > presets[closest].frequency)
383 closest = i;
387 if (closest < 0)
389 /* no presets in the given direction */
390 /* wrap around depending on direction */
391 if (direction < 0)
392 closest = highpreset;
393 else
394 closest = lowpreset;
397 return closest;
400 static void remember_frequency(void)
402 const struct fm_region_data * const fmr =
403 &fm_region_data[global_settings.fm_region];
404 global_status.last_frequency = (curr_freq - fmr->freq_min)
405 / fmr->freq_step;
406 status_save();
409 static void next_preset(int direction)
411 if (num_presets < 1)
412 return;
414 if (curr_preset == -1)
415 curr_preset = find_closest_preset(curr_freq, direction);
416 else
417 curr_preset = (curr_preset + direction + num_presets) % num_presets;
419 /* Must stay on the current grid for the region */
420 curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
422 tuner_set(RADIO_FREQUENCY, curr_freq);
423 remember_frequency();
426 /* Step to the next or previous frequency */
427 static int step_freq(int freq, int direction)
429 const struct fm_region_data * const fmr =
430 &fm_region_data[global_settings.fm_region];
432 freq += direction*fmr->freq_step;
434 /* Wrap first or snapping to grid will not let us on the band extremes */
435 if (freq > fmr->freq_max)
436 freq = direction > 0 ? fmr->freq_min : fmr->freq_max;
437 else if (freq < fmr->freq_min)
438 freq = direction < 0 ? fmr->freq_max : fmr->freq_min;
439 else
440 freq = snap_freq_to_grid(freq);
442 return freq;
445 /* Step to the next or previous station */
446 static void next_station(int direction)
448 if (direction != 0 && radio_mode != RADIO_SCAN_MODE)
450 next_preset(direction);
451 return;
454 curr_freq = step_freq(curr_freq, direction);
456 if (radio_status == FMRADIO_PLAYING)
457 tuner_set(RADIO_MUTE, 1);
459 tuner_set(RADIO_FREQUENCY, curr_freq);
461 if (radio_status == FMRADIO_PLAYING)
462 tuner_set(RADIO_MUTE, 0);
464 curr_preset = find_preset(curr_freq);
465 remember_frequency();
468 /* Ends an in-progress search */
469 static void end_search(void)
471 if (search_dir != 0 && radio_status == FMRADIO_PLAYING)
472 tuner_set(RADIO_MUTE, 0);
473 search_dir = 0;
476 /* Speak a frequency. */
477 static void talk_freq(int freq, bool enqueue)
479 freq /= 10000;
480 talk_number(freq / 100, enqueue);
481 talk_id(LANG_POINT, true);
482 talk_number(freq % 100 / 10, true);
483 if (freq % 10)
484 talk_number(freq % 10, true);
487 /* Speak a preset by number or by spelling its name, depending on settings. */
488 static void talk_preset(int preset, bool fallback, bool enqueue)
490 if (global_settings.talk_file == 1) /* number */
491 talk_number(preset + 1, enqueue);
492 else
493 { /* spell */
494 if(presets[preset].name[0])
495 talk_spell(presets[preset].name, enqueue);
496 else if(fallback)
497 talk_freq(presets[preset].frequency, enqueue);
501 /* Skin stuff */
502 extern struct wps_state wps_state; /* from wps.c */
503 static struct gui_wps fms_skin[NB_SCREENS] = {{ .data = NULL }};
504 static struct wps_data fms_skin_data[NB_SCREENS] = {{ .wps_loaded = 0 }};
505 static struct wps_sync_data fms_skin_sync_data = { .do_full_update = false };
508 void fms_data_load(enum screen_type screen, const char *buf, bool isfile)
510 struct wps_data *data = fms_skin[screen].data;
511 int success;
512 success = buf && skin_data_load(screen, data, buf, isfile);
514 if (!success ) /* load the default */
516 const char default_fms[] = "%Sx|Station:| %tf\n"
517 "%?ts<%Sx|Stereo||%Sx|Mono|>\n"
518 "%?tm<%Sx|Mode:| %Sx|Scan||%Sx|Preset|: %Ti. %?Tn<%Tn|%Tf>>\n"
519 "%pb\n"
520 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
521 "%?Rr<%Sx|Time:| %Rh:%Rn:%Rs|"
522 "%?St|prerecording time|<%Sx|Prerecord Time| %Rs|%pm>>\n"
523 #endif
524 #ifdef HAVE_RDS_CAP
525 "\n%s%ty\n"
526 "%s%tz\n"
527 #endif
529 skin_data_load(screen, data, default_fms, false);
532 enum fms_exiting {
533 FMS_EXIT,
534 FMS_ENTER
536 void fms_fix_displays(enum fms_exiting toggle_state)
538 int i;
539 FOR_NB_SCREENS(i)
541 if (toggle_state == FMS_ENTER)
543 viewportmanager_theme_enable(i, skin_has_sbs(i, fms_skin[i].data), NULL);
544 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
545 screens[i].backdrop_show(fms_skin[i].data->backdrop);
546 #endif
547 screens[i].clear_display();
548 /* force statusbar/skin update since we just cleared the whole screen */
549 send_event(GUI_EVENT_ACTIONUPDATE, (void*)1);
551 else
553 screens[i].stop_scroll();
554 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
555 screens[i].backdrop_show(sb_get_backdrop(i));
556 #endif
557 viewportmanager_theme_undo(i, skin_has_sbs(i, fms_skin[i].data));
563 void fms_skin_init(void)
565 int i;
566 FOR_NB_SCREENS(i)
568 #ifdef HAVE_ALBUMART
569 fms_skin_data[i].albumart = NULL;
570 fms_skin_data[i].playback_aa_slot = -1;
571 #endif
572 fms_skin[i].data = &fms_skin_data[i];
573 fms_skin[i].display = &screens[i];
574 /* Currently no seperate wps_state needed/possible
575 so use the only available ( "global" ) one */
576 fms_skin[i].state = &wps_state;
577 fms_skin[i].sync_data = &fms_skin_sync_data;
581 int radio_screen(void)
583 bool done = false;
584 int ret_val = GO_TO_ROOT;
585 int button;
586 int i;
587 bool stereo = false, last_stereo = false;
588 bool update_screen = true, restore = true;
589 bool screen_freeze = false;
590 bool keep_playing = false;
591 bool talk = false;
592 #ifdef FM_RECORD_DBLPRE
593 int lastbutton = BUTTON_NONE;
594 unsigned long rec_lastclick = 0;
595 #endif
596 #if CONFIG_CODEC != SWCODEC
597 bool have_recorded = false;
598 int timeout = current_tick + HZ/10;
599 unsigned int last_seconds = 0;
600 #ifndef SIMULATOR
601 unsigned int seconds = 0;
602 struct audio_recording_options rec_options;
603 #endif
604 #endif /* CONFIG_CODEC != SWCODEC */
605 #ifndef HAVE_NOISY_IDLE_MODE
606 int button_timeout = current_tick + (2*HZ);
607 #endif
609 /* change status to "in screen" */
610 in_screen = true;
612 if(num_presets <= 0)
614 radio_load_presets(global_settings.fmr_file);
617 if(radio_status == FMRADIO_OFF)
618 audio_stop();
619 #ifndef SIMULATOR
621 #if CONFIG_CODEC != SWCODEC
622 if(rec_create_directory() > 0)
623 have_recorded = true;
625 audio_init_recording(talk_get_bufsize());
627 sound_settings_apply();
628 /* Yes, we use the D/A for monitoring */
629 peak_meter_playback(true);
631 peak_meter_enable(true);
633 rec_init_recording_options(&rec_options);
634 rec_options.rec_source = AUDIO_SRC_LINEIN;
635 rec_set_recording_options(&rec_options);
637 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
638 sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
640 #endif /* CONFIG_CODEC != SWCODEC */
641 #endif /* ndef SIMULATOR */
643 /* turn on radio */
644 #if CONFIG_CODEC == SWCODEC
645 audio_set_input_source(AUDIO_SRC_FMRADIO,
646 (radio_status == FMRADIO_PAUSED) ?
647 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
648 #else
649 if (radio_status == FMRADIO_OFF)
650 radio_start();
651 #endif
653 if(num_presets < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN)))
654 scan_presets(NULL);
656 curr_preset = find_preset(curr_freq);
657 if(curr_preset != -1)
658 radio_mode = RADIO_PRESET_MODE;
660 #ifndef HAVE_NOISY_IDLE_MODE
661 cpu_idle_mode(true);
662 #endif
664 while(!done)
666 if(search_dir != 0)
668 curr_freq = step_freq(curr_freq, search_dir);
669 update_screen = true;
671 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
673 curr_preset = find_preset(curr_freq);
674 remember_frequency();
675 end_search();
676 talk = true;
678 trigger_cpu_boost();
681 if (!update_screen)
683 cancel_cpu_boost();
686 button = skin_wait_for_action(fms_skin, CONTEXT_FM,
687 update_screen ? TIMEOUT_NOBLOCK : HZ);
689 #ifndef HAVE_NOISY_IDLE_MODE
690 if (button != ACTION_NONE)
692 cpu_idle_mode(false);
693 button_timeout = current_tick + (2*HZ);
695 #endif
696 switch(button)
698 case ACTION_FM_STOP:
699 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
700 if(audio_status() == AUDIO_STATUS_RECORD)
702 audio_stop();
704 else
705 #endif
707 done = true;
708 if(presets_changed)
710 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
712 if(filepreset[0] == '\0')
713 save_preset_list();
714 else
715 radio_save_presets();
719 update_screen = true;
720 break;
722 #ifdef FM_RECORD
723 case ACTION_FM_RECORD:
724 #ifdef FM_RECORD_DBLPRE
725 if (lastbutton != ACTION_FM_RECORD_DBLPRE)
727 rec_lastclick = 0;
728 break;
730 if (current_tick - rec_lastclick > HZ/2)
732 rec_lastclick = current_tick;
733 break;
735 #endif /* FM_RECORD_DBLPRE */
736 #ifndef SIMULATOR
737 if(audio_status() == AUDIO_STATUS_RECORD)
739 rec_command(RECORDING_CMD_START_NEWFILE);
740 update_screen = true;
742 else
744 have_recorded = true;
745 rec_command(RECORDING_CMD_START);
746 update_screen = true;
748 #endif /* SIMULATOR */
749 last_seconds = 0;
750 break;
751 #endif /* #ifdef FM_RECORD */
753 case ACTION_FM_EXIT:
754 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
755 if(audio_status() == AUDIO_STATUS_RECORD)
756 audio_stop();
757 #endif
758 keep_playing = true;
759 done = true;
760 ret_val = GO_TO_ROOT;
761 if(presets_changed)
763 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
765 if(filepreset[0] == '\0')
766 save_preset_list();
767 else
768 radio_save_presets();
772 break;
774 case ACTION_STD_PREV:
775 case ACTION_STD_NEXT:
776 next_station(button == ACTION_STD_PREV ? -1 : 1);
777 end_search();
778 update_screen = true;
779 talk = true;
780 break;
782 case ACTION_STD_PREVREPEAT:
783 case ACTION_STD_NEXTREPEAT:
785 int dir = search_dir;
786 search_dir = button == ACTION_STD_PREVREPEAT ? -1 : 1;
787 if (radio_mode != RADIO_SCAN_MODE)
789 next_preset(search_dir);
790 end_search();
791 update_screen = true;
792 talk = true;
794 else if (dir == 0)
796 /* Starting auto scan */
797 tuner_set(RADIO_MUTE, 1);
798 update_screen = true;
800 break;
803 case ACTION_SETTINGS_INC:
804 case ACTION_SETTINGS_INCREPEAT:
805 global_settings.volume++;
806 setvol();
807 update_screen = true;
808 break;
810 case ACTION_SETTINGS_DEC:
811 case ACTION_SETTINGS_DECREPEAT:
812 global_settings.volume--;
813 setvol();
814 update_screen = true;
815 break;
817 case ACTION_FM_PLAY:
818 if (radio_status == FMRADIO_PLAYING)
819 radio_pause();
820 else
821 radio_start();
823 update_screen = true;
824 talk = false;
825 talk_shutup();
826 break;
828 case ACTION_FM_MENU:
829 fms_fix_displays(FMS_EXIT);
830 radio_menu();
831 curr_preset = find_preset(curr_freq);
832 update_screen = true;
833 restore = true;
834 break;
836 #ifdef FM_PRESET
837 case ACTION_FM_PRESET:
838 if(num_presets < 1)
840 splash(HZ, ID2P(LANG_FM_NO_PRESETS));
841 update_screen = true;
842 break;
844 fms_fix_displays(FMS_EXIT);
845 handle_radio_presets();
846 update_screen = true;
847 restore = true;
848 break;
849 #endif /* FM_PRESET */
851 #ifdef FM_FREEZE
852 case ACTION_FM_FREEZE:
853 if(!screen_freeze)
855 splash(HZ, str(LANG_FM_FREEZE));
856 screen_freeze = true;
858 else
860 update_screen = true;
861 screen_freeze = false;
863 break;
864 #endif /* FM_FREEZE */
866 case SYS_USB_CONNECTED:
867 #if CONFIG_CODEC != SWCODEC
868 /* Only accept USB connection when not recording */
869 if(audio_status() != AUDIO_STATUS_RECORD)
870 #endif
872 default_event_handler(SYS_USB_CONNECTED);
873 screen_freeze = true; /* Cosmetic: makes sure the
874 radio screen doesn't redraw */
875 done = true;
877 break;
879 #ifdef FM_MODE
880 case ACTION_FM_MODE:
881 if(radio_mode == RADIO_SCAN_MODE)
883 /* Force scan mode if there are no presets. */
884 if(num_presets > 0)
885 radio_mode = RADIO_PRESET_MODE;
887 else
888 radio_mode = RADIO_SCAN_MODE;
889 update_screen = true;
890 cond_talk_ids_fq(radio_mode ?
891 LANG_PRESET : LANG_RADIO_SCAN_MODE);
892 talk = true;
893 break;
894 #endif /* FM_MODE */
896 #ifdef FM_NEXT_PRESET
897 case ACTION_FM_NEXT_PRESET:
898 next_preset(1);
899 end_search();
900 update_screen = true;
901 talk = true;
902 break;
903 #endif
905 #ifdef FM_PREV_PRESET
906 case ACTION_FM_PREV_PRESET:
907 next_preset(-1);
908 end_search();
909 update_screen = true;
910 talk = true;
911 break;
912 #endif
914 default:
915 default_event_handler(button);
916 #ifdef HAVE_RDS_CAP
917 if (tuner_get(RADIO_EVENT))
918 update_screen = true;
919 #endif
920 if (!tuner_get(RADIO_PRESENT))
922 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
923 if(audio_status() == AUDIO_STATUS_RECORD)
924 audio_stop();
925 #endif
926 keep_playing = false;
927 done = true;
928 ret_val = GO_TO_ROOT;
929 if(presets_changed)
931 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
933 if(filepreset[0] == '\0')
934 save_preset_list();
935 else
936 radio_save_presets();
940 /* Clear the preset list on exit. */
941 clear_preset_list();
943 break;
944 } /*switch(button)*/
946 #ifdef FM_RECORD_DBLPRE
947 if (button != ACTION_NONE)
948 lastbutton = button;
949 #endif
951 #if CONFIG_CODEC != SWCODEC
952 peak_meter_peek();
953 #endif
955 if(!screen_freeze)
957 /* Only display the peak meter when not recording */
958 #if CONFIG_CODEC != SWCODEC
959 if(TIME_AFTER(current_tick, timeout))
961 timeout = current_tick + HZ;
962 #else /* SWCODEC */
964 #endif /* CONFIG_CODEC == SWCODEC */
966 /* keep "mono" from always being displayed when paused */
967 if (radio_status != FMRADIO_PAUSED)
969 stereo = tuner_get(RADIO_STEREO) &&
970 !global_settings.fm_force_mono;
972 if(stereo != last_stereo)
974 update_screen = true;
975 last_stereo = stereo;
980 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
981 seconds = audio_recorded_time() / HZ;
982 if (update_screen || seconds > last_seconds || restore)
984 last_seconds = seconds;
985 #else
986 if (update_screen || restore)
988 #endif
989 if (restore)
990 fms_fix_displays(FMS_ENTER);
991 FOR_NB_SCREENS(i)
992 skin_update(&fms_skin[i], WPS_REFRESH_ALL);
993 restore = false;
996 update_screen = false;
998 if (global_settings.talk_file && talk
999 && radio_status == FMRADIO_PAUSED)
1001 talk = false;
1002 bool enqueue = false;
1003 if (radio_mode == RADIO_SCAN_MODE)
1005 talk_freq(curr_freq, enqueue);
1006 enqueue = true;
1008 if (curr_preset >= 0)
1009 talk_preset(curr_preset, radio_mode == RADIO_PRESET_MODE,
1010 enqueue);
1013 #if CONFIG_CODEC != SWCODEC
1014 if(audio_status() & AUDIO_STATUS_ERROR)
1016 done = true;
1018 #endif
1020 #ifndef HAVE_NOISY_IDLE_MODE
1021 if (TIME_AFTER(current_tick, button_timeout))
1023 cpu_idle_mode(true);
1025 #endif
1026 } /*while(!done)*/
1028 #ifndef SIMULATOR
1029 #if CONFIG_CODEC != SWCODEC
1030 if(audio_status() & AUDIO_STATUS_ERROR)
1032 splash(0, str(LANG_DISK_FULL));
1033 audio_error_clear();
1035 while(1)
1037 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
1038 if(button == ACTION_FM_STOP)
1039 break;
1043 audio_init_playback();
1044 #endif /* CONFIG_CODEC != SWCODEC */
1046 sound_settings_apply();
1047 #endif /* SIMULATOR */
1049 if(keep_playing)
1051 /* Catch FMRADIO_PLAYING status for the sim. */
1052 #ifndef SIMULATOR
1053 #if CONFIG_CODEC != SWCODEC
1054 /* Enable the Left and right A/D Converter */
1055 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
1056 sound_default(SOUND_RIGHT_GAIN),
1057 AUDIO_GAIN_LINEIN);
1058 mas_codec_writereg(6, 0x4000);
1059 #endif
1060 end_search();
1061 #endif /* SIMULATOR */
1063 else
1065 #if CONFIG_CODEC == SWCODEC
1066 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
1067 #else
1068 radio_stop();
1069 #endif
1072 #ifndef HAVE_NOISY_IDLE_MODE
1073 cpu_idle_mode(false);
1074 #endif
1075 fms_fix_displays(FMS_EXIT);
1076 in_screen = false;
1077 #if CONFIG_CODEC != SWCODEC
1078 return have_recorded;
1079 #else
1080 return false;
1081 #endif
1082 } /* radio_screen */
1084 static void radio_save_presets(void)
1086 int fd;
1087 int i;
1089 fd = creat(filepreset, 0666);
1090 if(fd >= 0)
1092 for(i = 0;i < num_presets;i++)
1094 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
1096 close(fd);
1098 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
1099 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
1100 presets_changed = false;
1102 else
1104 splash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED));
1108 void radio_load_presets(char *filename)
1110 int fd;
1111 int rc;
1112 char buf[128];
1113 char *freq;
1114 char *name;
1115 bool done = false;
1116 int f;
1118 memset(presets, 0, sizeof(presets));
1119 num_presets = 0;
1121 /* No Preset in configuration. */
1122 if(filename[0] == '\0')
1124 filepreset[0] = '\0';
1125 return;
1127 /* Temporary preset, loaded until player shuts down. */
1128 else if(filename[0] == '/')
1129 strlcpy(filepreset, filename, sizeof(filepreset));
1130 /* Preset from default directory. */
1131 else
1132 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1133 FMPRESET_PATH, filename);
1135 fd = open_utf8(filepreset, O_RDONLY);
1136 if(fd >= 0)
1138 while(!done && num_presets < MAX_PRESETS)
1140 rc = read_line(fd, buf, 128);
1141 if(rc > 0)
1143 if(settings_parseline(buf, &freq, &name))
1145 f = atoi(freq);
1146 if(f) /* For backwards compatibility */
1148 struct fmstation * const fms = &presets[num_presets];
1149 fms->frequency = f;
1150 strlcpy(fms->name, name, MAX_FMPRESET_LEN+1);
1151 num_presets++;
1155 else
1156 done = true;
1158 close(fd);
1160 else /* invalid file name? */
1161 filepreset[0] = '\0';
1163 presets_loaded = num_presets > 0;
1164 presets_changed = false;
1168 static int radio_add_preset(void)
1170 char buf[MAX_FMPRESET_LEN + 1];
1172 if(num_presets < MAX_PRESETS)
1174 buf[0] = '\0';
1176 if (!kbd_input(buf, MAX_FMPRESET_LEN + 1))
1178 struct fmstation * const fms = &presets[num_presets];
1179 strcpy(fms->name, buf);
1180 fms->frequency = curr_freq;
1181 num_presets++;
1182 presets_changed = true;
1183 presets_loaded = num_presets > 0;
1184 return true;
1187 else
1189 splash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS));
1191 return false;
1194 /* needed to know which preset we are edit/delete-ing */
1195 static int selected_preset = -1;
1196 static int radio_edit_preset(void)
1198 char buf[MAX_FMPRESET_LEN + 1];
1200 if (num_presets > 0)
1202 struct fmstation * const fms = &presets[selected_preset];
1204 strcpy(buf, fms->name);
1206 if (!kbd_input(buf, MAX_FMPRESET_LEN + 1))
1208 strcpy(fms->name, buf);
1209 presets_changed = true;
1213 return 1;
1216 static int radio_delete_preset(void)
1218 if (num_presets > 0)
1220 struct fmstation * const fms = &presets[selected_preset];
1222 if (selected_preset >= --num_presets)
1223 selected_preset = num_presets - 1;
1225 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1226 (uintptr_t)fms);
1228 if (curr_preset >= num_presets)
1229 --curr_preset;
1232 /* Don't ask to save when all presets are deleted. */
1233 presets_changed = num_presets > 0;
1235 if (!presets_changed)
1237 /* The preset list will be cleared, switch to Scan Mode. */
1238 radio_mode = RADIO_SCAN_MODE;
1239 curr_preset = -1;
1240 presets_loaded = false;
1243 return 1;
1246 static int load_preset_list(void)
1248 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1251 static int save_preset_list(void)
1253 if(num_presets > 0)
1255 bool bad_file_name = true;
1257 if(!dir_exists(FMPRESET_PATH)) /* Check if there is preset folder */
1258 mkdir(FMPRESET_PATH);
1260 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1261 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1263 while(bad_file_name)
1265 if(!kbd_input(filepreset, sizeof(filepreset)))
1267 /* check the name: max MAX_FILENAME (20) chars */
1268 char* p2;
1269 char* p1;
1270 int len;
1271 p1 = strrchr(filepreset, '/');
1272 p2 = p1;
1273 while((p1) && (*p2) && (*p2 != '.'))
1274 p2++;
1275 len = (int)(p2-p1) - 1;
1276 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1278 /* no slash, too long or too short */
1279 splash(HZ, ID2P(LANG_INVALID_FILENAME));
1281 else
1283 /* add correct extension (easier to always write)
1284 at this point, p2 points to 0 or the extension dot */
1285 *p2 = '\0';
1286 strcat(filepreset,".fmr");
1287 bad_file_name = false;
1288 radio_save_presets();
1291 else
1293 /* user aborted */
1294 return false;
1298 else
1299 splash(HZ, ID2P(LANG_FM_NO_PRESETS));
1301 return true;
1304 static int clear_preset_list(void)
1306 /* Clear all the preset entries */
1307 memset(presets, 0, sizeof (presets));
1309 num_presets = 0;
1310 presets_loaded = false;
1311 /* The preset list will be cleared switch to Scan Mode. */
1312 radio_mode = RADIO_SCAN_MODE;
1313 curr_preset = -1;
1314 presets_changed = false; /* Don't ask to save when clearing the list. */
1316 return true;
1319 MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1320 ID2P(LANG_FM_EDIT_PRESET),
1321 radio_edit_preset, NULL, NULL, Icon_NOICON);
1322 MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1323 ID2P(LANG_FM_DELETE_PRESET),
1324 radio_delete_preset, NULL, NULL, Icon_NOICON);
1325 static int radio_preset_callback(int action,
1326 const struct menu_item_ex *this_item)
1328 if (action == ACTION_STD_OK)
1329 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1330 return action;
1331 (void)this_item;
1333 MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_PRESET),
1334 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1335 &radio_delete_preset_item);
1336 /* present a list of preset stations */
1337 static const char* presets_get_name(int selected_item, void *data,
1338 char *buffer, size_t buffer_len)
1340 (void)data;
1341 struct fmstation *p = &presets[selected_item];
1342 if(p->name[0])
1343 return p->name;
1344 int freq = p->frequency / 10000;
1345 int frac = freq % 100;
1346 freq /= 100;
1347 snprintf(buffer, buffer_len,
1348 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1349 return buffer;
1352 static int presets_speak_name(int selected_item, void * data)
1354 (void)data;
1355 talk_preset(selected_item, true, false);
1356 return 0;
1359 static int handle_radio_presets(void)
1361 struct gui_synclist lists;
1362 int result = 0;
1363 int action = ACTION_NONE;
1364 #ifdef HAVE_BUTTONBAR
1365 struct gui_buttonbar buttonbar;
1366 #endif
1368 if(presets_loaded == false)
1369 return result;
1371 #ifdef HAVE_BUTTONBAR
1372 gui_buttonbar_init(&buttonbar);
1373 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1374 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1375 str(LANG_FM_BUTTONBAR_EXIT),
1376 str(LANG_FM_BUTTONBAR_ACTION));
1377 gui_buttonbar_draw(&buttonbar);
1378 #endif
1379 gui_synclist_init(&lists, presets_get_name, NULL, false, 1, NULL);
1380 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
1381 gui_synclist_set_icon_callback(&lists, NULL);
1382 if(global_settings.talk_file)
1383 gui_synclist_set_voice_callback(&lists, presets_speak_name);
1384 gui_synclist_set_nb_items(&lists, num_presets);
1385 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1386 gui_synclist_speak_item(&lists);
1388 while (result == 0)
1390 gui_synclist_draw(&lists);
1391 list_do_action(CONTEXT_STD, TIMEOUT_BLOCK,
1392 &lists, &action, LIST_WRAP_UNLESS_HELD);
1393 switch (action)
1395 case ACTION_STD_MENU:
1396 if (radio_add_preset())
1398 gui_synclist_set_nb_items(&lists, num_presets);
1399 gui_synclist_select_item(&lists, num_presets - 1);
1401 break;
1402 case ACTION_STD_CANCEL:
1403 result = 1;
1404 break;
1405 case ACTION_STD_OK:
1406 curr_preset = gui_synclist_get_sel_pos(&lists);
1407 curr_freq = presets[curr_preset].frequency;
1408 next_station(0);
1409 remember_frequency();
1410 result = 1;
1411 break;
1412 case ACTION_F3:
1413 case ACTION_STD_CONTEXT:
1414 selected_preset = gui_synclist_get_sel_pos(&lists);
1415 do_menu(&handle_radio_preset_menu, NULL, NULL, false);
1416 gui_synclist_set_nb_items(&lists, num_presets);
1417 gui_synclist_select_item(&lists, selected_preset);
1418 gui_synclist_speak_item(&lists);
1419 break;
1420 default:
1421 if(default_event_handler(action) == SYS_USB_CONNECTED)
1422 result = 2;
1425 return result - 1;
1428 void toggle_mono_mode(bool mono)
1430 tuner_set(RADIO_FORCE_MONO, mono);
1433 void set_radio_region(int region)
1435 #ifdef HAVE_RADIO_REGION
1436 tuner_set(RADIO_REGION, region);
1437 #endif
1438 next_station(0);
1439 remember_frequency();
1440 (void)region;
1443 MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
1444 MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
1446 #ifndef FM_MODE
1447 static char* get_mode_text(int selected_item, void * data, char *buffer)
1449 (void)selected_item;
1450 (void)data;
1451 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_MODE),
1452 radio_mode ? str(LANG_PRESET) :
1453 str(LANG_RADIO_SCAN_MODE));
1454 return buffer;
1456 static int toggle_radio_mode(void)
1458 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
1459 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
1460 return 0;
1462 MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1463 toggle_radio_mode, NULL,
1464 get_mode_text, NULL, NULL, NULL, Icon_NOICON);
1465 #endif
1467 static int scan_presets(void *viewports)
1469 bool do_scan = true;
1470 int i;
1471 struct viewport *vp = (struct viewport *)viewports;
1473 FOR_NB_SCREENS(i)
1474 screens[i].set_viewport(vp?&vp[i]:NULL);
1475 if(num_presets > 0) /* Do that to avoid 2 questions. */
1476 do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS));
1478 if(do_scan)
1480 const struct fm_region_data * const fmr =
1481 &fm_region_data[global_settings.fm_region];
1483 curr_freq = fmr->freq_min;
1484 num_presets = 0;
1485 memset(presets, 0, sizeof(presets));
1487 tuner_set(RADIO_MUTE, 1);
1489 while(curr_freq <= fmr->freq_max)
1491 int freq, frac;
1492 if(num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1493 break;
1495 freq = curr_freq / 10000;
1496 frac = freq % 100;
1497 freq /= 100;
1499 splashf(0, str(LANG_FM_SCANNING), freq, frac);
1501 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
1503 /* add preset */
1504 presets[num_presets].name[0] = '\0';
1505 presets[num_presets].frequency = curr_freq;
1506 num_presets++;
1509 curr_freq += fmr->freq_step;
1512 if (radio_status == FMRADIO_PLAYING)
1513 tuner_set(RADIO_MUTE, 0);
1515 presets_changed = true;
1517 FOR_NB_SCREENS(i)
1519 screens[i].clear_viewport();
1520 screens[i].update_viewport();
1523 if(num_presets > 0)
1525 curr_freq = presets[0].frequency;
1526 radio_mode = RADIO_PRESET_MODE;
1527 presets_loaded = true;
1528 next_station(0);
1530 else
1532 /* Wrap it to beginning or we'll be past end of band */
1533 presets_loaded = false;
1534 next_station(1);
1537 return true;
1541 #ifdef HAVE_RECORDING
1543 #if defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC
1544 #define FM_RECORDING_SCREEN
1545 static int fm_recording_screen(void)
1547 bool ret;
1549 /* switch recording source to FMRADIO for the duration */
1550 int rec_source = global_settings.rec_source;
1551 global_settings.rec_source = AUDIO_SRC_FMRADIO;
1552 ret = recording_screen(true);
1554 /* safe to reset as changing sources is prohibited here */
1555 global_settings.rec_source = rec_source;
1557 return ret;
1560 #endif /* defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC */
1562 #if defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC
1563 #define FM_RECORDING_SETTINGS
1564 static int fm_recording_settings(void)
1566 bool ret = recording_menu(true);
1568 #if CONFIG_CODEC != SWCODEC
1569 if (!ret)
1571 struct audio_recording_options rec_options;
1572 rec_init_recording_options(&rec_options);
1573 rec_options.rec_source = AUDIO_SRC_LINEIN;
1574 rec_set_recording_options(&rec_options);
1576 #endif
1578 return ret;
1581 #endif /* defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC */
1582 #endif /* HAVE_RECORDING */
1584 #ifdef FM_RECORDING_SCREEN
1585 MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING),
1586 fm_recording_screen, NULL, NULL, Icon_Recording);
1587 #endif
1588 #ifdef FM_RECORDING_SETTINGS
1589 MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1590 fm_recording_settings, NULL, NULL, Icon_Recording);
1591 #endif
1592 #ifndef FM_PRESET
1593 MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
1594 handle_radio_presets, NULL, NULL, Icon_NOICON);
1595 #endif
1596 #ifndef FM_PRESET_ADD
1597 MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1598 radio_add_preset, NULL, NULL, Icon_NOICON);
1599 #endif
1602 MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1603 load_preset_list, NULL, NULL, Icon_NOICON);
1604 MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1605 save_preset_list, NULL, NULL, Icon_NOICON);
1606 MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1607 clear_preset_list, NULL, NULL, Icon_NOICON);
1608 MENUITEM_FUNCTION(scan_presets_item, MENU_FUNC_USEPARAM,
1609 ID2P(LANG_FM_SCAN_PRESETS),
1610 scan_presets, NULL, NULL, Icon_NOICON);
1612 MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
1613 Icon_Radio_screen,
1614 #ifndef FM_PRESET
1615 &radio_presets_item,
1616 #endif
1617 #ifndef FM_PRESET_ADD
1618 &radio_addpreset_item,
1619 #endif
1620 &presetload_item, &presetsave_item, &presetclear_item,
1621 &force_mono,
1622 #ifndef FM_MODE
1623 &radio_mode_item,
1624 #endif
1625 &set_region, &sound_settings,
1626 #ifdef FM_RECORDING_SCREEN
1627 &recscreen_item,
1628 #endif
1629 #ifdef FM_RECORDING_SETTINGS
1630 &recsettings_item,
1631 #endif
1632 &scan_presets_item);
1633 /* main menu of the radio screen */
1634 static bool radio_menu(void)
1636 return do_menu(&radio_settings_menu, NULL, NULL, false) ==
1637 MENU_ATTACHED_USB;
1640 #endif