Accept FS #7303 by Christoph Reiter: add context menu to FM radio entry in main menu...
[Rockbox.git] / apps / recorder / radio.c
blob06332898263a7ec61f4dd058441a2b47e4fe683b
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2003 Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "config.h"
21 #include <stdio.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include "sprintf.h"
25 #include "mas.h"
26 #include "settings.h"
27 #include "button.h"
28 #include "status.h"
29 #include "thread.h"
30 #include "mpeg.h"
31 #include "audio.h"
32 #include "mp3_playback.h"
33 #include "ctype.h"
34 #include "file.h"
35 #include "errno.h"
36 #include "atoi.h"
37 #include "string.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 #include "recording.h"
49 #include "talk.h"
50 #include "tuner.h"
51 #include "power.h"
52 #include "sound.h"
53 #include "screen_access.h"
54 #include "statusbar.h"
55 #include "textarea.h"
56 #include "splash.h"
57 #include "yesno.h"
58 #include "buttonbar.h"
59 #include "power.h"
60 #include "tree.h"
61 #include "dir.h"
62 #include "action.h"
63 #include "list.h"
64 #include "menus/exported_menus.h"
65 #include "root_menu.h"
67 #if CONFIG_TUNER
69 #if CONFIG_KEYPAD == RECORDER_PAD
70 #define FM_RECORD
71 #define FM_PRESET_ADD
72 #define FM_PRESET_ACTION
73 #define FM_PRESET
74 #define FM_MODE
76 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
77 #define FM_PRESET
78 #define FM_MODE
79 #define FM_NEXT_PRESET
80 #define FM_PREV_PRESET
82 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
83 #define FM_PRESET
84 #define FM_MODE
85 /* This should be removeable if the whole tuning thing is sorted out since
86 proper tuning quiets the screen almost entirely in that extreme measures
87 have to be taken to hear any interference. */
88 #define HAVE_NOISY_IDLE_MODE
90 #elif CONFIG_KEYPAD == ONDIO_PAD
91 #define FM_RECORD_DBLPRE
92 #define FM_RECORD
93 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
94 #define FM_MENU
95 #define FM_PRESET
96 #define FM_STOP
97 #define FM_MODE
98 #define FM_EXIT
99 #define FM_PLAY
100 #endif
102 #define RADIO_SCAN_MODE 0
103 #define RADIO_PRESET_MODE 1
105 static const struct fm_region_setting fm_region[] = {
106 /* Note: Desriptive strings are just for display atm and are not compiled. */
107 [REGION_EUROPE] =
108 FM_REGION_ENTRY("Europe", 87500000, 108000000, 50000, 0, 0),
109 [REGION_US_CANADA] =
110 FM_REGION_ENTRY("US/Canada", 87900000, 107900000, 200000, 1, 0),
111 [REGION_JAPAN] =
112 FM_REGION_ENTRY("Japan", 76000000, 90000000, 100000, 0, 1),
113 [REGION_KOREA] =
114 FM_REGION_ENTRY("Korea", 87500000, 108000000, 100000, 0, 0),
117 static int curr_preset = -1;
118 static int curr_freq;
119 static int radio_mode = RADIO_SCAN_MODE;
120 static int search_dir = 0;
122 static int radio_status = FMRADIO_OFF;
123 static bool in_screen = false;
125 #define MAX_PRESETS 64
126 static bool presets_loaded = false, presets_changed = false;
127 static struct fmstation presets[MAX_PRESETS];
129 static char filepreset[MAX_PATH]; /* preset filename variable */
131 static int num_presets = 0; /* The number of presets in the preset list */
133 static void radio_save_presets(void);
134 static int handle_radio_presets(void);
135 static bool radio_menu(void);
136 static int radio_add_preset(void);
137 static int save_preset_list(void);
138 static int load_preset_list(void);
139 static int clear_preset_list(void);
141 static int scan_presets(void);
143 /* Function to manipulate all yesno dialogues.
144 This function needs the output text as an argument. */
145 static bool yesno_pop(char* text)
147 int i;
148 char *lines[]={text};
149 struct text_message message={lines, 1};
150 bool ret = (gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
151 FOR_NB_SCREENS(i)
152 gui_textarea_clear(&screens[i]);
153 return ret;
156 void radio_init(void)
158 tuner_init();
159 radio_stop();
162 int get_radio_status(void)
164 return radio_status;
167 bool in_radio_screen(void)
169 return in_screen;
172 /* TODO: Move some more of the control functionality to an HAL and clean up the
173 mess */
175 /* secret flag for starting paused - prevents unmute */
176 #define FMRADIO_START_PAUSED 0x8000
177 void radio_start(void)
179 const struct fm_region_setting *fmr;
180 bool start_paused;
181 #if CONFIG_TUNER != LV24020LP
182 int mute_timeout;
183 #endif
185 if(radio_status == FMRADIO_PLAYING)
186 return;
188 fmr = &fm_region[global_settings.fm_region];
190 start_paused = radio_status & FMRADIO_START_PAUSED;
191 /* clear flag before any yielding */
192 radio_status &= ~FMRADIO_START_PAUSED;
194 if(radio_status == FMRADIO_OFF)
195 radio_power(true);
197 curr_freq = global_status.last_frequency
198 * fmr->freq_step + fmr->freq_min;
200 radio_set(RADIO_SLEEP, 0); /* wake up the tuner */
201 #if (CONFIG_TUNER & LV24020LP)
202 radio_set(RADIO_REGION, global_settings.fm_region);
203 radio_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
204 #endif
205 radio_set(RADIO_FREQUENCY, curr_freq);
207 #if CONFIG_TUNER != LV24020LP
209 if(radio_status == FMRADIO_OFF)
211 #if (CONFIG_TUNER & S1A0903X01)
212 radio_set(RADIO_IF_MEASUREMENT, 0);
213 radio_set(RADIO_SENSITIVITY, 0);
214 #endif
215 radio_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
216 #if (CONFIG_TUNER & TEA5767)
217 radio_set(RADIO_SET_DEEMPHASIS, fmr->deemphasis);
218 radio_set(RADIO_SET_BAND, fmr->band);
219 #endif
220 mute_timeout = current_tick + 1*HZ;
222 else
224 /* paused */
225 mute_timeout = current_tick + 2*HZ;
228 while(!radio_get(RADIO_STEREO) && !radio_get(RADIO_TUNED))
230 if(TIME_AFTER(current_tick, mute_timeout))
231 break;
232 yield();
234 #endif /* CONFIG_TUNER != LV24020LP */
236 /* keep radio from sounding initially */
237 if(!start_paused)
238 radio_set(RADIO_MUTE, 0);
240 radio_status = FMRADIO_PLAYING;
241 } /* radio_start */
243 void radio_pause(void)
245 if(radio_status == FMRADIO_PAUSED)
246 return;
248 if(radio_status == FMRADIO_OFF)
250 radio_status |= FMRADIO_START_PAUSED;
251 radio_start();
254 radio_set(RADIO_MUTE, 1);
255 radio_set(RADIO_SLEEP, 1);
257 radio_status = FMRADIO_PAUSED;
258 } /* radio_pause */
260 void radio_stop(void)
262 if(radio_status == FMRADIO_OFF)
263 return;
265 radio_set(RADIO_MUTE, 1);
266 radio_set(RADIO_SLEEP, 1); /* low power mode, if available */
267 radio_status = FMRADIO_OFF;
268 radio_power(false); /* status update, power off if avail. */
269 } /* radio_stop */
271 bool radio_hardware_present(void)
273 #ifdef HAVE_TUNER_PWR_CTRL
274 bool ret;
275 bool fmstatus = radio_power(true); /* power it up */
276 ret = radio_get(RADIO_PRESENT);
277 radio_power(fmstatus); /* restore previous state */
278 return ret;
279 #else
280 return radio_get(RADIO_PRESENT);
281 #endif
284 /* Keep freq on the grid for the current region */
285 static int snap_freq_to_grid(int freq)
287 const struct fm_region_setting * const fmr =
288 &fm_region[global_settings.fm_region];
290 /* Range clamp if out of range or just round to nearest */
291 if (freq < fmr->freq_min)
292 freq = fmr->freq_min;
293 else if (freq > fmr->freq_max)
294 freq = fmr->freq_max;
295 else
296 freq = (freq - fmr->freq_min + fmr->freq_step/2) /
297 fmr->freq_step * fmr->freq_step + fmr->freq_min;
299 return freq;
302 /* Find a matching preset to freq */
303 static int find_preset(int freq)
305 int i;
306 if(num_presets < 1)
307 return -1;
308 for(i = 0;i < MAX_PRESETS;i++)
310 if(freq == presets[i].frequency)
311 return i;
314 return -1;
317 /* Return the first preset encountered in the search direction with
318 wraparound. */
319 static int find_closest_preset(int freq, int direction)
321 int i;
323 if (direction == 0) /* direction == 0 isn't really used */
324 return 0;
326 for (i = 0; i < MAX_PRESETS; i++)
328 int preset_frequency = presets[i].frequency;
330 if (preset_frequency == freq)
331 return i; /* Exact match = stop */
332 /* Stop when the preset frequency exeeds freq so that we can
333 pick the correct one based on direction */
334 if (preset_frequency > freq)
335 break;
338 /* wrap around depending on direction */
339 if (i == 0 || i >= num_presets - 1)
340 i = direction < 0 ? num_presets - 1 : 0;
341 else if (direction < 0)
342 i--; /* use previous */
344 return i;
347 static void remember_frequency(void)
349 const struct fm_region_setting * const fmr =
350 &fm_region[global_settings.fm_region];
352 global_status.last_frequency = (curr_freq - fmr->freq_min)
353 / fmr->freq_step;
354 status_save();
357 static void next_preset(int direction)
359 if (num_presets < 1)
360 return;
362 if (curr_preset == -1)
363 curr_preset = find_closest_preset(curr_freq, direction);
364 else
365 curr_preset = (curr_preset + direction + num_presets) % num_presets;
367 /* Must stay on the current grid for the region */
368 curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
370 radio_set(RADIO_FREQUENCY, curr_freq);
371 remember_frequency();
374 /* Step to the next or previous frequency */
375 static int step_freq(int freq, int direction)
377 const struct fm_region_setting * const fmr =
378 &fm_region[global_settings.fm_region];
380 freq += direction*fmr->freq_step;
382 /* Wrap first or snapping to grid will not let us on the band extremes */
383 if (freq > fmr->freq_max)
384 freq = direction > 0 ? fmr->freq_min : fmr->freq_max;
385 else if (freq < fmr->freq_min)
386 freq = direction < 0 ? fmr->freq_max : fmr->freq_min;
387 else
388 freq = snap_freq_to_grid(freq);
390 return freq;
393 /* Step to the next or previous station */
394 static void next_station(int direction)
396 if (direction != 0 && radio_mode != RADIO_SCAN_MODE)
398 next_preset(direction);
399 return;
402 curr_freq = step_freq(curr_freq, direction);
404 if (radio_status == FMRADIO_PLAYING)
405 radio_set(RADIO_MUTE, 1);
407 radio_set(RADIO_FREQUENCY, curr_freq);
409 if (radio_status == FMRADIO_PLAYING)
410 radio_set(RADIO_MUTE, 0);
412 curr_preset = find_preset(curr_freq);
413 remember_frequency();
416 /* Ends an in-progress search */
417 static void end_search(void)
419 if (search_dir != 0 && radio_status == FMRADIO_PLAYING)
420 radio_set(RADIO_MUTE, 0);
421 search_dir = 0;
424 int radio_screen(void)
426 char buf[MAX_PATH];
427 bool done = false;
428 int ret_val = GO_TO_ROOT;
429 int button;
430 int i;
431 bool stereo = false, last_stereo = false;
432 int fh;
433 int top_of_screen = 0;
434 bool update_screen = true;
435 bool screen_freeze = false;
436 bool keep_playing = false;
437 bool statusbar = global_settings.statusbar;
438 #ifdef FM_RECORD_DBLPRE
439 int lastbutton = BUTTON_NONE;
440 unsigned long rec_lastclick = 0;
441 #endif
442 #if CONFIG_CODEC != SWCODEC
443 bool have_recorded = false;
444 int timeout = current_tick + HZ/10;
445 unsigned int seconds = 0;
446 unsigned int last_seconds = 0;
447 int hours, minutes;
448 struct audio_recording_options rec_options;
449 #endif /* CONFIG_CODEC != SWCODEC */
450 #ifndef HAVE_NOISY_IDLE_MODE
451 int button_timeout = current_tick + (2*HZ);
452 #endif
453 #ifdef HAS_BUTTONBAR
454 struct gui_buttonbar buttonbar;
455 gui_buttonbar_init(&buttonbar);
456 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
457 #endif
459 /* change status to "in screen" */
460 in_screen = true;
462 /* always display status bar in radio screen for now */
463 global_settings.statusbar = true;
464 FOR_NB_SCREENS(i)
466 gui_textarea_clear(&screens[i]);
467 screen_set_xmargin(&screens[i],0);
470 gui_syncstatusbar_draw(&statusbars,true);
472 fh = font_get(FONT_UI)->height;
474 /* Adjust for font size, trying to center the information vertically */
475 if(fh < 10)
476 top_of_screen = 1;
478 if(num_presets <= 0)
480 memset(presets, 0, sizeof(presets));
481 radio_load_presets(global_settings.fmr_file);
484 if(radio_status == FMRADIO_OFF)
485 audio_stop();
486 #ifndef SIMULATOR
488 #if CONFIG_CODEC != SWCODEC
489 if(rec_create_directory() > 0)
490 have_recorded = true;
492 audio_init_recording(talk_get_bufsize());
494 sound_settings_apply();
495 /* Yes, we use the D/A for monitoring */
496 peak_meter_playback(true);
498 peak_meter_enabled = true;
500 rec_init_recording_options(&rec_options);
501 rec_options.rec_source = AUDIO_SRC_LINEIN;
502 rec_set_recording_options(&rec_options);
504 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
505 sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
507 #endif /* CONFIG_CODEC != SWCODEC */
508 #endif /* ndef SIMULATOR */
510 /* turn on radio */
511 #if CONFIG_CODEC == SWCODEC
512 audio_set_input_source(AUDIO_SRC_FMRADIO,
513 (radio_status == FMRADIO_PAUSED) ?
514 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
515 #else
516 if (radio_status == FMRADIO_OFF)
517 radio_start();
518 #endif
520 if(num_presets < 1 && yesno_pop(str(LANG_FM_FIRST_AUTOSCAN)))
521 scan_presets();
523 curr_preset = find_preset(curr_freq);
524 if(curr_preset != -1)
525 radio_mode = RADIO_PRESET_MODE;
527 #ifdef HAS_BUTTONBAR
528 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
529 str(LANG_FM_BUTTONBAR_PRESETS), str(LANG_FM_BUTTONBAR_RECORD));
530 #endif
532 #ifndef HAVE_NOISY_IDLE_MODE
533 cpu_idle_mode(true);
534 #endif
536 while(!done)
538 if(search_dir != 0)
540 curr_freq = step_freq(curr_freq, search_dir);
541 update_screen = true;
543 if(radio_set(RADIO_SCAN_FREQUENCY, curr_freq))
545 curr_preset = find_preset(curr_freq);
546 remember_frequency();
547 end_search();
550 trigger_cpu_boost();
553 #if CONFIG_CODEC != SWCODEC
554 /* TODO: Can we timeout at HZ when recording since peaks aren't
555 displayed? This should quiet recordings too. */
556 button = get_action(CONTEXT_FM,
557 update_screen ? TIMEOUT_NOBLOCK : HZ / PEAK_METER_FPS);
558 #else
559 button = get_action(CONTEXT_FM,
560 update_screen ? TIMEOUT_NOBLOCK : HZ);
561 #endif
563 #ifndef HAVE_NOISY_IDLE_MODE
564 if (button != ACTION_NONE)
566 cpu_idle_mode(false);
567 button_timeout = current_tick + (2*HZ);
569 #endif
570 switch(button)
572 case ACTION_FM_STOP:
573 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
574 if(audio_status() == AUDIO_STATUS_RECORD)
576 audio_stop();
578 else
579 #endif
581 done = true;
582 if(presets_changed)
584 if(yesno_pop(str(LANG_FM_SAVE_CHANGES)))
586 if(filepreset[0] == '\0')
587 save_preset_list();
588 else
589 radio_save_presets();
592 /* Clear the preset list on exit. */
593 clear_preset_list();
595 update_screen = true;
596 break;
598 #ifdef FM_RECORD
599 case ACTION_FM_RECORD:
600 #ifdef FM_RECORD_DBLPRE
601 if (lastbutton != ACTION_FM_RECORD_DBLPRE)
603 rec_lastclick = 0;
604 break;
606 if (current_tick - rec_lastclick > HZ/2)
608 rec_lastclick = current_tick;
609 break;
611 #endif /* FM_RECORD_DBLPRE */
612 #ifndef SIMULATOR
613 if(audio_status() == AUDIO_STATUS_RECORD)
615 rec_new_file();
616 update_screen = true;
618 else
620 have_recorded = true;
621 rec_record();
622 update_screen = true;
624 #endif /* SIMULATOR */
625 last_seconds = 0;
626 break;
627 #endif /* #ifdef FM_RECORD */
629 case ACTION_FM_EXIT:
630 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
631 if(audio_status() == AUDIO_STATUS_RECORD)
632 audio_stop();
633 #endif
634 keep_playing = true;
635 done = true;
636 ret_val = GO_TO_ROOT;
637 if(presets_changed)
639 if(yesno_pop(str(LANG_FM_SAVE_CHANGES)))
641 if(filepreset[0] == '\0')
642 save_preset_list();
643 else
644 radio_save_presets();
648 /* Clear the preset list on exit. */
649 clear_preset_list();
651 break;
653 case ACTION_STD_PREV:
654 case ACTION_STD_NEXT:
655 next_station(button == ACTION_STD_PREV ? -1 : 1);
656 end_search();
657 update_screen = true;
658 break;
660 case ACTION_STD_PREVREPEAT:
661 case ACTION_STD_NEXTREPEAT:
663 int dir = search_dir;
664 search_dir = button == ACTION_STD_PREVREPEAT ? -1 : 1;
665 if (radio_mode != RADIO_SCAN_MODE)
667 next_preset(search_dir);
668 end_search();
669 update_screen = true;
671 else if (dir == 0)
673 /* Starting auto scan */
674 radio_set(RADIO_MUTE, 1);
675 update_screen = true;
677 break;
680 case ACTION_SETTINGS_INC:
681 case ACTION_SETTINGS_INCREPEAT:
682 global_settings.volume++;
683 setvol();
684 update_screen = true;
685 break;
687 case ACTION_SETTINGS_DEC:
688 case ACTION_SETTINGS_DECREPEAT:
689 global_settings.volume--;
690 setvol();
691 update_screen = true;
692 break;
694 case ACTION_FM_PLAY:
695 if (radio_status == FMRADIO_PLAYING)
696 radio_pause();
697 else
698 radio_start();
700 update_screen = true;
701 break;
703 case ACTION_FM_MENU:
704 radio_menu();
705 curr_preset = find_preset(curr_freq);
706 FOR_NB_SCREENS(i){
707 struct screen *sc = &screens[i];
708 gui_textarea_clear(sc);
709 screen_set_xmargin(sc, 0);
711 #ifdef HAS_BUTTONBAR
712 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
713 str(LANG_FM_BUTTONBAR_PRESETS),
714 str(LANG_FM_BUTTONBAR_RECORD));
715 #endif
716 update_screen = true;
717 break;
719 #ifdef FM_PRESET
720 case ACTION_FM_PRESET:
721 if(num_presets < 1)
723 gui_syncsplash(HZ, str(LANG_FM_NO_PRESETS));
724 update_screen = true;
725 FOR_NB_SCREENS(i)
727 struct screen *sc = &screens[i];
728 gui_textarea_clear(sc);
729 screen_set_xmargin(sc, 0);
730 gui_textarea_update(sc);
733 break;
735 handle_radio_presets();
736 FOR_NB_SCREENS(i)
738 struct screen *sc = &screens[i];
739 gui_textarea_clear(sc);
740 screen_set_xmargin(sc, 0);
741 gui_textarea_update(sc);
743 #ifdef HAS_BUTTONBAR
744 gui_buttonbar_set(&buttonbar,
745 str(LANG_BUTTONBAR_MENU),
746 str(LANG_FM_BUTTONBAR_PRESETS),
747 str(LANG_FM_BUTTONBAR_RECORD));
748 #endif
749 update_screen = true;
750 break;
751 #endif /* FM_PRESET */
753 #ifdef FM_FREEZE
754 case ACTION_FM_FREEZE:
755 if(!screen_freeze)
757 gui_syncsplash(HZ, str(LANG_FM_FREEZE));
758 screen_freeze = true;
760 else
762 update_screen = true;
763 screen_freeze = false;
765 break;
766 #endif /* FM_FREEZE */
768 case SYS_USB_CONNECTED:
769 #if CONFIG_CODEC != SWCODEC
770 /* Only accept USB connection when not recording */
771 if(audio_status() != AUDIO_STATUS_RECORD)
772 #endif
774 default_event_handler(SYS_USB_CONNECTED);
775 screen_freeze = true; /* Cosmetic: makes sure the
776 radio screen doesn't redraw */
777 done = true;
779 break;
781 #ifdef FM_MODE
782 case ACTION_FM_MODE:
783 if(radio_mode == RADIO_SCAN_MODE)
785 /* Force scan mode if there are no presets. */
786 if(num_presets > 0)
787 radio_mode = RADIO_PRESET_MODE;
789 else
790 radio_mode = RADIO_SCAN_MODE;
791 update_screen = true;
792 break;
793 #endif /* FM_MODE */
795 #ifdef FM_NEXT_PRESET
796 case ACTION_FM_NEXT_PRESET:
797 next_preset(1);
798 end_search();
799 update_screen = true;
800 break;
801 #endif
803 #ifdef FM_PREV_PRESET
804 case ACTION_FM_PREV_PRESET:
805 next_preset(-1);
806 end_search();
807 update_screen = true;
808 break;
809 #endif
811 default:
812 default_event_handler(button);
813 break;
814 } /*switch(button)*/
816 #ifdef FM_RECORD_DBLPRE
817 if (button != ACTION_NONE)
818 lastbutton = button;
819 #endif
821 #if CONFIG_CODEC != SWCODEC
822 peak_meter_peek();
823 #endif
825 if(!screen_freeze)
827 /* Only display the peak meter when not recording */
828 #if CONFIG_CODEC != SWCODEC
829 if(!audio_status())
831 FOR_NB_SCREENS(i)
833 peak_meter_screen(&screens[i],0,
834 STATUSBAR_HEIGHT + fh*(top_of_screen + 4), fh);
835 screens[i].update_rect(0, STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
836 screens[i].width, fh);
840 if(TIME_AFTER(current_tick, timeout))
842 timeout = current_tick + HZ;
843 #else /* SWCODEC */
845 #endif /* CONFIG_CODEC == SWCODEC */
847 /* keep "mono" from always being displayed when paused */
848 if (radio_status != FMRADIO_PAUSED)
850 stereo = radio_get(RADIO_STEREO) &&
851 !global_settings.fm_force_mono;
853 if(stereo != last_stereo)
855 update_screen = true;
856 last_stereo = stereo;
861 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
862 seconds = audio_recorded_time() / HZ;
863 if (update_screen || seconds > last_seconds)
865 last_seconds = seconds;
866 #else
867 if (update_screen)
869 #endif
870 int freq;
872 FOR_NB_SCREENS(i)
873 screens[i].setfont(FONT_UI);
875 snprintf(buf, 128, curr_preset >= 0 ? "%d. %s" : " ",
876 curr_preset + 1, presets[curr_preset].name);
878 FOR_NB_SCREENS(i)
879 screens[i].puts_scroll(0, top_of_screen, buf);
881 freq = curr_freq / 10000;
882 snprintf(buf, 128, str(LANG_FM_STATION), freq / 100, freq % 100);
883 FOR_NB_SCREENS(i)
884 screens[i].puts_scroll(0, top_of_screen + 1, buf);
886 snprintf(buf, 128, stereo?str(LANG_CHANNEL_STEREO):
887 str(LANG_CHANNEL_MONO));
888 FOR_NB_SCREENS(i)
889 screens[i].puts_scroll(0, top_of_screen + 2, buf);
891 snprintf(buf, 128, "%s %s", str(LANG_FM_TUNE_MODE),
892 radio_mode ? str(LANG_RADIO_PRESET_MODE) :
893 str(LANG_RADIO_SCAN_MODE));
894 FOR_NB_SCREENS(i)
895 screens[i].puts_scroll(0, top_of_screen + 3, buf);
897 #if CONFIG_CODEC != SWCODEC
898 if(audio_status() == AUDIO_STATUS_RECORD)
900 hours = seconds / 3600;
901 minutes = (seconds - (hours * 3600)) / 60;
902 snprintf(buf, 32, "%s %02d:%02d:%02d",
903 str(LANG_RECORDING_TIME),
904 hours, minutes, seconds%60);
905 FOR_NB_SCREENS(i)
906 screens[i].puts_scroll(0, top_of_screen + 4, buf);
908 else
910 if(rec_options.rec_prerecord_time)
912 snprintf(buf, 32, "%s %02d",
913 str(LANG_RECORD_PRERECORD), seconds%60);
914 FOR_NB_SCREENS(i)
915 screens[i].puts_scroll(0, top_of_screen + 4, buf);
918 #endif /* CONFIG_CODEC != SWCODEC */
920 #ifdef HAS_BUTTONBAR
921 gui_buttonbar_draw(&buttonbar);
922 #endif
923 FOR_NB_SCREENS(i)
924 gui_textarea_update(&screens[i]);
926 /* Only force the redraw if update_screen is true */
927 gui_syncstatusbar_draw(&statusbars,true);
930 update_screen = false;
932 #if CONFIG_CODEC != SWCODEC
933 if(audio_status() & AUDIO_STATUS_ERROR)
935 done = true;
937 #endif
939 #ifndef HAVE_NOISY_IDLE_MODE
940 if (TIME_AFTER(current_tick, button_timeout))
942 cpu_idle_mode(true);
944 #endif
945 } /*while(!done)*/
947 #ifndef SIMULATOR
948 #if CONFIG_CODEC != SWCODEC
949 if(audio_status() & AUDIO_STATUS_ERROR)
951 gui_syncsplash(0, str(LANG_DISK_FULL));
952 gui_syncstatusbar_draw(&statusbars,true);
953 FOR_NB_SCREENS(i)
954 gui_textarea_update(&screens[i]);
955 audio_error_clear();
957 while(1)
959 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
960 if(button == ACTION_FM_STOP)
961 break;
965 audio_init_playback();
966 #endif /* CONFIG_CODEC != SWCODEC */
968 sound_settings_apply();
969 #endif /* SIMULATOR */
971 if(keep_playing)
973 /* Catch FMRADIO_PLAYING status for the sim. */
974 #ifndef SIMULATOR
975 #if CONFIG_CODEC != SWCODEC
976 /* Enable the Left and right A/D Converter */
977 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
978 sound_default(SOUND_RIGHT_GAIN),
979 AUDIO_GAIN_LINEIN);
980 mas_codec_writereg(6, 0x4000);
981 #endif
982 end_search();
983 #endif /* SIMULATOR */
985 else
987 #if CONFIG_CODEC == SWCODEC
988 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
989 #else
990 radio_stop();
991 #endif
994 #ifndef HAVE_NOISY_IDLE_MODE
995 cpu_idle_mode(false);
996 #endif
998 /* restore status bar settings */
999 global_settings.statusbar = statusbar;
1001 in_screen = false;
1002 #if CONFIG_CODEC != SWCODEC
1003 return have_recorded;
1004 #else
1005 return false;
1006 #endif
1007 } /* radio_screen */
1009 static void radio_save_presets(void)
1011 int fd;
1012 int i;
1014 fd = creat(filepreset);
1015 if(fd >= 0)
1017 for(i = 0;i < num_presets;i++)
1019 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
1021 close(fd);
1023 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
1024 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
1025 presets_changed = false;
1027 else
1029 gui_syncsplash(HZ, str(LANG_FM_PRESET_SAVE_FAILED));
1033 void radio_load_presets(char *filename)
1035 int fd;
1036 int rc;
1037 char buf[128];
1038 char *freq;
1039 char *name;
1040 bool done = false;
1041 int f;
1043 memset(presets, 0, sizeof(presets));
1044 num_presets = 0;
1046 /* No Preset in configuration. */
1047 if(filename[0] == '\0')
1049 filepreset[0] = '\0';
1050 return;
1052 /* Temporary preset, loaded until player shuts down. */
1053 else if(filename[0] == '/')
1054 strncpy(filepreset, filename, sizeof(filepreset));
1055 /* Preset from default directory. */
1056 else
1057 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1058 FMPRESET_PATH, filename);
1060 fd = open(filepreset, O_RDONLY);
1061 if(fd >= 0)
1063 while(!done && num_presets < MAX_PRESETS)
1065 rc = read_line(fd, buf, 128);
1066 if(rc > 0)
1068 if(settings_parseline(buf, &freq, &name))
1070 f = atoi(freq);
1071 if(f) /* For backwards compatibility */
1073 struct fmstation * const fms = &presets[num_presets];
1074 fms->frequency = f;
1075 strncpy(fms->name, name, MAX_FMPRESET_LEN);
1076 fms->name[MAX_FMPRESET_LEN] = '\0';
1077 num_presets++;
1081 else
1082 done = true;
1084 close(fd);
1086 else /* invalid file name? */
1087 filepreset[0] = '\0';
1089 presets_loaded = num_presets > 0;
1090 presets_changed = false;
1094 static int radio_add_preset(void)
1096 char buf[MAX_FMPRESET_LEN + 1];
1098 if(num_presets < MAX_PRESETS)
1100 memset(buf, 0, MAX_FMPRESET_LEN);
1102 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1104 struct fmstation * const fms = &presets[num_presets];
1105 buf[MAX_FMPRESET_LEN] = '\0';
1106 strcpy(fms->name, buf);
1107 fms->frequency = curr_freq;
1108 num_presets++;
1109 presets_changed = true;
1110 presets_loaded = num_presets > 0;
1113 else
1115 gui_syncsplash(HZ, str(LANG_FM_NO_FREE_PRESETS));
1117 return true;
1120 /* needed to know which preset we are edit/delete-ing */
1121 static int selected_preset = -1;
1122 static int radio_edit_preset(void)
1124 char buf[MAX_FMPRESET_LEN + 1];
1126 if (num_presets > 0)
1128 struct fmstation * const fms = &presets[selected_preset];
1130 strncpy(buf, fms->name, MAX_FMPRESET_LEN);
1132 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1134 buf[MAX_FMPRESET_LEN] = '\0';
1135 strcpy(fms->name, buf);
1136 presets_changed = true;
1140 return 1;
1143 static int radio_delete_preset(void)
1145 if (num_presets > 0)
1147 struct fmstation * const fms = &presets[selected_preset];
1149 if (selected_preset >= --num_presets)
1150 selected_preset = num_presets - 1;
1152 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1153 (uintptr_t)fms);
1157 /* Don't ask to save when all presets are deleted. */
1158 presets_changed = num_presets > 0;
1160 if (!presets_changed)
1162 /* The preset list will be cleared, switch to Scan Mode. */
1163 radio_mode = RADIO_SCAN_MODE;
1164 presets_loaded = false;
1167 return 1;
1170 static int load_preset_list(void)
1172 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1175 static int save_preset_list(void)
1177 if(num_presets > 0)
1179 bool bad_file_name = true;
1181 if(!opendir(FMPRESET_PATH)) /* Check if there is preset folder */
1182 mkdir(FMPRESET_PATH);
1184 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1185 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1187 while(bad_file_name)
1189 if(!kbd_input(filepreset, sizeof(filepreset)))
1191 /* check the name: max MAX_FILENAME (20) chars */
1192 char* p2;
1193 char* p1;
1194 int len;
1195 p1 = strrchr(filepreset, '/');
1196 p2 = p1;
1197 while((p1) && (*p2) && (*p2 != '.'))
1198 p2++;
1199 len = (int)(p2-p1) - 1;
1200 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1202 /* no slash, too long or too short */
1203 gui_syncsplash(HZ, str(LANG_INVALID_FILENAME));
1205 else
1207 /* add correct extension (easier to always write)
1208 at this point, p2 points to 0 or the extension dot */
1209 *p2 = '\0';
1210 strcat(filepreset,".fmr");
1211 bad_file_name = false;
1212 radio_save_presets();
1215 else
1217 /* user aborted */
1218 return false;
1222 else
1223 gui_syncsplash(HZ, str(LANG_FM_NO_PRESETS));
1225 return true;
1228 static int clear_preset_list(void)
1230 /* Clear all the preset entries */
1231 memset(presets, 0, sizeof (presets));
1233 num_presets = 0;
1234 presets_loaded = false;
1235 /* The preset list will be cleared switch to Scan Mode. */
1236 radio_mode = RADIO_SCAN_MODE;
1238 presets_changed = false; /* Don't ask to save when clearing the list. */
1240 return true;
1243 MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1244 ID2P(LANG_FM_EDIT_PRESET),
1245 radio_edit_preset, NULL, NULL, Icon_NOICON);
1246 MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1247 ID2P(LANG_FM_DELETE_PRESET),
1248 radio_delete_preset, NULL, NULL, Icon_NOICON);
1249 int radio_preset_callback(int action, const struct menu_item_ex *this_item)
1251 if (action == ACTION_STD_OK)
1252 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1253 return action;
1254 (void)this_item;
1256 MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_FM_BUTTONBAR_PRESETS),
1257 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1258 &radio_delete_preset_item);
1259 /* present a list of preset stations */
1260 char * presets_get_name(int selected_item, void * data, char *buffer)
1262 (void)data;
1263 (void)buffer;
1264 return presets[selected_item].name;
1267 static int handle_radio_presets(void)
1269 struct gui_synclist lists;
1270 int result = 0;
1271 int action = ACTION_NONE;
1272 #ifdef HAS_BUTTONBAR
1273 struct gui_buttonbar buttonbar;
1274 #endif
1276 if(presets_loaded == false)
1277 return result;
1279 #ifdef HAS_BUTTONBAR
1280 gui_buttonbar_init(&buttonbar);
1281 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1282 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1283 str(LANG_FM_BUTTONBAR_EXIT),
1284 str(LANG_FM_BUTTONBAR_ACTION));
1285 gui_buttonbar_draw(&buttonbar);
1286 #endif
1287 gui_synclist_init(&lists, presets_get_name, NULL, false, 1);
1288 gui_synclist_set_title(&lists, str(LANG_FM_BUTTONBAR_PRESETS), NOICON);
1289 gui_synclist_set_icon_callback(&lists, NULL);
1290 gui_synclist_set_nb_items(&lists, num_presets);
1291 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1293 action_signalscreenchange();
1294 while (result == 0)
1296 gui_synclist_draw(&lists);
1297 gui_syncstatusbar_draw(&statusbars, true);
1298 action = get_action(CONTEXT_STD, HZ);
1300 gui_synclist_do_button(&lists, action, LIST_WRAP_UNLESS_HELD);
1301 switch (action)
1303 case ACTION_STD_MENU:
1304 radio_add_preset();
1305 break;
1306 case ACTION_STD_CANCEL:
1307 result = 1;
1308 break;
1309 case ACTION_STD_OK:
1310 curr_preset = gui_synclist_get_sel_pos(&lists);
1311 curr_freq = presets[curr_preset].frequency;
1312 next_station(0);
1313 remember_frequency();
1314 result = 1;
1315 break;
1316 case ACTION_F3:
1317 case ACTION_STD_CONTEXT:
1318 selected_preset = gui_synclist_get_sel_pos(&lists);
1319 do_menu(&handle_radio_preset_menu, NULL);
1320 break;
1321 default:
1322 if(default_event_handler(action) == SYS_USB_CONNECTED)
1323 result = 2;
1326 action_signalscreenchange();
1327 return result - 1;
1330 void toggle_mono_mode(bool mono)
1332 radio_set(RADIO_FORCE_MONO, mono);
1335 void set_radio_region(int region)
1337 #if (CONFIG_TUNER & LV24020LP)
1338 radio_set(RADIO_REGION, global_settings.fm_region);
1339 #endif
1340 #if (CONFIG_TUNER & TEA5767)
1341 radio_set(RADIO_SET_DEEMPHASIS,
1342 fm_region[region].deemphasis);
1343 radio_set(RADIO_SET_BAND, fm_region[region].band);
1344 #else
1345 (void)region;
1346 #endif
1347 next_station(0);
1348 remember_frequency();
1351 MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
1352 MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
1354 #ifndef FM_MODE
1355 char* get_mode_text(int selected_item, void * data, char *buffer)
1357 (void)selected_item;
1358 (void)data;
1359 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_FM_TUNE_MODE),
1360 radio_mode ? str(LANG_RADIO_PRESET_MODE) :
1361 str(LANG_RADIO_SCAN_MODE));
1362 return buffer;
1364 static int toggle_radio_mode(void)
1366 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
1367 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
1368 return 0;
1370 MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1371 toggle_radio_mode, NULL,
1372 get_mode_text, NULL, NULL, Icon_NOICON);
1373 #endif
1375 static int scan_presets(void)
1377 bool do_scan = true;
1379 if(num_presets > 0) /* Do that to avoid 2 questions. */
1380 do_scan = yesno_pop(str(LANG_FM_CLEAR_PRESETS));
1382 if(do_scan)
1384 const struct fm_region_setting * const fmr =
1385 &fm_region[global_settings.fm_region];
1386 char buf[MAX_FMPRESET_LEN + 1];
1387 int i;
1389 curr_freq = fmr->freq_min;
1390 num_presets = 0;
1391 memset(presets, 0, sizeof(presets));
1392 radio_set(RADIO_MUTE, 1);
1394 while(curr_freq <= fmr->freq_max)
1396 int freq, frac;
1397 if (num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1398 break;
1400 freq = curr_freq / 10000;
1401 frac = freq % 100;
1402 freq /= 100;
1404 snprintf(buf, MAX_FMPRESET_LEN, str(LANG_FM_SCANNING), freq, frac);
1405 gui_syncsplash(0, buf);
1407 if(radio_set(RADIO_SCAN_FREQUENCY, curr_freq))
1409 /* add preset */
1410 snprintf(buf, MAX_FMPRESET_LEN,
1411 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1412 strcpy(presets[num_presets].name,buf);
1413 presets[num_presets].frequency = curr_freq;
1414 num_presets++;
1417 curr_freq += fmr->freq_step;
1420 if (radio_status == FMRADIO_PLAYING)
1421 radio_set(RADIO_MUTE, 0);
1423 presets_changed = true;
1425 FOR_NB_SCREENS(i)
1427 gui_textarea_clear(&screens[i]);
1428 screen_set_xmargin(&screens[i],0);
1429 gui_textarea_update(&screens[i]);
1432 if(num_presets > 0)
1434 curr_freq = presets[0].frequency;
1435 radio_mode = RADIO_PRESET_MODE;
1436 presets_loaded = true;
1437 next_station(0);
1439 else
1441 /* Wrap it to beginning or we'll be past end of band */
1442 presets_loaded = false;
1443 next_station(1);
1446 return true;
1450 #ifdef HAVE_RECORDING
1452 #if defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC
1453 #define FM_RECORDING_SCREEN
1454 static int fm_recording_screen(void)
1456 bool ret;
1458 /* switch recording source to FMRADIO for the duration */
1459 int rec_source = global_settings.rec_source;
1460 global_settings.rec_source = AUDIO_SRC_FMRADIO;
1462 /* clearing queue seems to cure a spontaneous abort during record */
1463 action_signalscreenchange();
1465 ret = recording_screen(true);
1467 /* safe to reset as changing sources is prohibited here */
1468 global_settings.rec_source = rec_source;
1470 return ret;
1473 #endif /* defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC */
1475 #if defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC
1476 #define FM_RECORDING_SETTINGS
1477 static int fm_recording_settings(void)
1479 bool ret = recording_menu(true);
1481 #if CONFIG_CODEC != SWCODEC
1482 if (!ret)
1484 struct audio_recording_options rec_options;
1485 rec_init_recording_options(&rec_options);
1486 rec_options.rec_source = AUDIO_SRC_LINEIN;
1487 rec_set_recording_options(&rec_options);
1489 #endif
1491 return ret;
1494 #endif /* defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC */
1495 #endif /* HAVE_RECORDING */
1497 #ifdef FM_RECORDING_SCREEN
1498 MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING_MENU),
1499 fm_recording_screen, NULL, NULL, Icon_Recording);
1500 #endif
1501 #ifdef FM_RECORDING_SETTINGS
1502 MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1503 fm_recording_settings, NULL, NULL, Icon_Recording);
1504 #endif
1505 #ifndef FM_PRESET
1506 MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_FM_BUTTONBAR_PRESETS),
1507 handle_radio_presets, NULL, NULL, Icon_NOICON);
1508 #endif
1509 #ifndef FM_PRESET_ADD
1510 MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1511 radio_add_preset, NULL, NULL, Icon_NOICON);
1512 #endif
1515 MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1516 load_preset_list, NULL, NULL, Icon_NOICON);
1517 MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1518 save_preset_list, NULL, NULL, Icon_NOICON);
1519 MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1520 clear_preset_list, NULL, NULL, Icon_NOICON);
1521 MENUITEM_FUNCTION(scan_presets_item, 0, ID2P(LANG_FM_SCAN_PRESETS),
1522 scan_presets, NULL, NULL, Icon_NOICON);
1524 MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
1525 Icon_Radio_screen,
1526 #ifndef FM_PRESET
1527 &radio_presets_item,
1528 #endif
1529 #ifndef FM_PRESET_ADD
1530 &radio_addpreset_item,
1531 #endif
1532 &presetload_item, &presetsave_item, &presetclear_item,
1533 &force_mono,
1534 #ifndef FM_MODE
1535 &radio_mode_item,
1536 #endif
1537 &set_region, &sound_settings,
1538 #ifdef FM_RECORDING_SCREEN
1539 &recscreen_item,
1540 #endif
1541 #ifdef FM_RECORDING_SETTINGS
1542 &recsettings_item,
1543 #endif
1544 &scan_presets_item);
1545 /* main menu of the radio screen */
1546 static bool radio_menu(void)
1548 return do_menu(&radio_settings_menu, NULL) == MENU_ATTACHED_USB;
1551 #endif