Exit the preset context menu correctly after editing/deleting preset
[Rockbox.git] / apps / recorder / radio.c
blob5e0185d71c20685b64245dfa157243eb90eb8c93
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 #endif
95 #define RADIO_SCAN_MODE 0
96 #define RADIO_PRESET_MODE 1
98 static const struct fm_region_setting fm_region[] = {
99 /* Note: Desriptive strings are just for display atm and are not compiled. */
100 FM_REGION_ENTRY("Europe", 87500000, 108000000, 50000, 0, 0),
101 FM_REGION_ENTRY("US/Canada", 87900000, 107900000, 200000, 1, 0),
102 FM_REGION_ENTRY("Japan", 76000000, 90000000, 100000, 0, 1),
103 FM_REGION_ENTRY("Korea", 87500000, 108000000, 100000, 0, 0),
106 static int curr_preset = -1;
107 static int curr_freq;
108 static int radio_mode = RADIO_SCAN_MODE;
110 static int radio_status = FMRADIO_OFF;
111 static bool in_screen = false;
113 #define MAX_PRESETS 64
114 static bool presets_loaded = false, presets_changed = false;
115 static struct fmstation presets[MAX_PRESETS];
117 static char filepreset[MAX_PATH]; /* preset filename variable */
119 static int num_presets = 0; /* The number of presets in the preset list */
121 static void radio_save_presets(void);
122 static int handle_radio_presets(void);
123 static bool radio_menu(void);
124 static int radio_add_preset(void);
125 static int save_preset_list(void);
126 static int load_preset_list(void);
127 static int clear_preset_list(void);
129 static int scan_presets(void);
131 /* Function to manipulate all yesno dialogues.
132 This function needs the output text as an argument. */
133 static bool yesno_pop(char* text)
135 int i;
136 char *lines[]={text};
137 struct text_message message={lines, 1};
138 bool ret = (gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
139 FOR_NB_SCREENS(i)
140 gui_textarea_clear(&screens[i]);
141 return ret;
144 void radio_init(void)
146 tuner_init();
147 radio_stop();
150 int get_radio_status(void)
152 return radio_status;
155 bool in_radio_screen(void)
157 return in_screen;
160 /* secret flag for starting paused - prevents unmute */
161 #define FMRADIO_START_PAUSED 0x8000
162 void radio_start(void)
164 const struct fm_region_setting *fmr;
165 bool start_paused;
166 int mute_timeout;
168 if(radio_status == FMRADIO_PLAYING)
169 return;
171 fmr = &fm_region[global_settings.fm_region];
173 start_paused = radio_status & FMRADIO_START_PAUSED;
174 /* clear flag before any yielding */
175 radio_status &= ~FMRADIO_START_PAUSED;
177 if(radio_status == FMRADIO_OFF)
178 radio_power(true);
180 curr_freq = global_status.last_frequency
181 * fmr->freq_step + fmr->freq_min;
183 radio_set(RADIO_SLEEP, 0); /* wake up the tuner */
184 radio_set(RADIO_FREQUENCY, curr_freq);
186 if(radio_status == FMRADIO_OFF)
188 #if (CONFIG_TUNER & S1A0903X01)
189 radio_set(RADIO_IF_MEASUREMENT, 0);
190 radio_set(RADIO_SENSITIVITY, 0);
191 #endif
192 radio_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
193 #if (CONFIG_TUNER & TEA5767)
194 radio_set(RADIO_SET_DEEMPHASIS, fmr->deemphasis);
195 radio_set(RADIO_SET_BAND, fmr->band);
196 #endif
197 mute_timeout = current_tick + 1*HZ;
199 else
201 /* paused */
202 mute_timeout = current_tick + 2*HZ;
205 while(!radio_get(RADIO_STEREO) && !radio_get(RADIO_TUNED))
207 if(TIME_AFTER(current_tick, mute_timeout))
208 break;
209 yield();
212 /* keep radio from sounding initially */
213 if(!start_paused)
214 radio_set(RADIO_MUTE, 0);
216 radio_status = FMRADIO_PLAYING;
217 } /* radio_start */
219 void radio_pause(void)
221 if(radio_status == FMRADIO_PAUSED)
222 return;
224 if(radio_status == FMRADIO_OFF)
226 radio_status |= FMRADIO_START_PAUSED;
227 radio_start();
230 radio_set(RADIO_MUTE, 1);
231 radio_set(RADIO_SLEEP, 1);
233 radio_status = FMRADIO_PAUSED;
234 } /* radio_pause */
236 void radio_stop(void)
238 if(radio_status == FMRADIO_OFF)
239 return;
241 radio_set(RADIO_MUTE, 1);
242 radio_set(RADIO_SLEEP, 1); /* low power mode, if available */
243 radio_status = FMRADIO_OFF;
244 radio_power(false); /* status update, power off if avail. */
245 } /* radio_stop */
247 bool radio_hardware_present(void)
249 #ifdef HAVE_TUNER_PWR_CTRL
250 bool ret;
251 bool fmstatus = radio_power(true); /* power it up */
252 ret = radio_get(RADIO_PRESENT);
253 radio_power(fmstatus); /* restore previous state */
254 return ret;
255 #else
256 return radio_get(RADIO_PRESENT);
257 #endif
260 /* Keep freq on the grid for the current region */
261 static int snap_freq_to_grid(int freq)
263 const struct fm_region_setting * const fmr =
264 &fm_region[global_settings.fm_region];
266 /* Range clamp if out of range or just round to nearest */
267 if (freq < fmr->freq_min)
268 freq = fmr->freq_min;
269 else if (freq > fmr->freq_max)
270 freq = fmr->freq_max;
271 else
272 freq = (freq - fmr->freq_min + fmr->freq_step/2) /
273 fmr->freq_step * fmr->freq_step + fmr->freq_min;
275 return freq;
278 /* Find a matching preset to freq */
279 static int find_preset(int freq)
281 int i;
282 if(num_presets < 1)
283 return -1;
284 for(i = 0;i < MAX_PRESETS;i++)
286 if(freq == presets[i].frequency)
287 return i;
290 return -1;
293 /* Return the first preset encountered in the search direction with
294 wraparound. */
295 static int find_closest_preset(int freq, int direction)
297 int i;
299 if (direction == 0) /* direction == 0 isn't really used */
300 return 0;
302 for (i = 0; i < MAX_PRESETS; i++)
304 int preset_frequency = presets[i].frequency;
306 if (preset_frequency == freq)
307 return i; /* Exact match = stop */
308 /* Stop when the preset frequency exeeds freq so that we can
309 pick the correct one based on direction */
310 if (preset_frequency > freq)
311 break;
314 /* wrap around depending on direction */
315 if (i == 0 || i >= num_presets - 1)
316 i = direction < 0 ? num_presets - 1 : 0;
317 else if (direction < 0)
318 i--; /* use previous */
320 return i;
323 static void remember_frequency(void)
325 const struct fm_region_setting * const fmr =
326 &fm_region[global_settings.fm_region];
328 global_status.last_frequency = (curr_freq - fmr->freq_min)
329 / fmr->freq_step;
330 status_save();
333 static void next_preset(int direction)
335 if (num_presets < 1)
336 return;
338 if (curr_preset == -1)
339 curr_preset = find_closest_preset(curr_freq, direction);
340 else
341 curr_preset = (curr_preset + direction + num_presets) % num_presets;
343 /* Must stay on the current grid for the region */
344 curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
346 radio_set(RADIO_FREQUENCY, curr_freq);
347 remember_frequency();
350 /* Step to the next or previous frequency */
351 static int step_freq(int freq, int direction)
353 const struct fm_region_setting * const fmr =
354 &fm_region[global_settings.fm_region];
356 freq += direction*fmr->freq_step;
358 /* Wrap first or snapping to grid will not let us on the band extremes */
359 if (freq > fmr->freq_max)
360 freq = direction > 0 ? fmr->freq_min : fmr->freq_max;
361 else if (freq < fmr->freq_min)
362 freq = direction < 0 ? fmr->freq_max : fmr->freq_min;
363 else
364 freq = snap_freq_to_grid(freq);
366 return freq;
369 /* Step to the next or previous station */
370 static void next_station(int direction)
372 if (direction != 0 && radio_mode != RADIO_SCAN_MODE)
374 next_preset(direction);
375 return;
378 curr_freq = step_freq(curr_freq, direction);
380 if (radio_status == FMRADIO_PLAYING)
381 radio_set(RADIO_MUTE, 1);
383 radio_set(RADIO_FREQUENCY, curr_freq);
385 if (radio_status == FMRADIO_PLAYING)
386 radio_set(RADIO_MUTE, 0);
388 curr_preset = find_preset(curr_freq);
389 remember_frequency();
392 int radio_screen(void)
394 char buf[MAX_PATH];
395 bool done = false;
396 int ret_val = GO_TO_ROOT;
397 int button;
398 int i;
399 int search_dir = 0;
400 bool stereo = false, last_stereo = false;
401 int fh;
402 int top_of_screen = 0;
403 bool update_screen = true;
404 bool screen_freeze = false;
405 bool keep_playing = false;
406 bool statusbar = global_settings.statusbar;
407 #ifdef FM_RECORD_DBLPRE
408 int lastbutton = BUTTON_NONE;
409 unsigned long rec_lastclick = 0;
410 #endif
411 #if CONFIG_CODEC != SWCODEC
412 bool have_recorded = false;
413 int timeout = current_tick + HZ/10;
414 unsigned int seconds = 0;
415 unsigned int last_seconds = 0;
416 int hours, minutes;
417 struct audio_recording_options rec_options;
418 #endif /* CONFIG_CODEC != SWCODEC */
419 #ifndef HAVE_NOISY_IDLE_MODE
420 int button_timeout = current_tick + (2*HZ);
421 #endif
422 #ifdef HAS_BUTTONBAR
423 struct gui_buttonbar buttonbar;
424 gui_buttonbar_init(&buttonbar);
425 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
426 #endif
428 /* Ends an in-progress search - needs access to search_dir */
429 void end_search(void)
431 if (search_dir != 0 && radio_status == FMRADIO_PLAYING)
432 radio_set(RADIO_MUTE, 0);
433 search_dir = 0;
436 /* change status to "in screen" */
437 in_screen = true;
439 /* always display status bar in radio screen for now */
440 global_settings.statusbar = true;
441 FOR_NB_SCREENS(i)
443 gui_textarea_clear(&screens[i]);
444 screen_set_xmargin(&screens[i],0);
447 gui_syncstatusbar_draw(&statusbars,true);
449 fh = font_get(FONT_UI)->height;
451 /* Adjust for font size, trying to center the information vertically */
452 if(fh < 10)
453 top_of_screen = 1;
455 if(num_presets <= 0)
457 memset(presets, 0, sizeof(presets));
458 radio_load_presets(global_settings.fmr_file);
461 if(radio_status == FMRADIO_OFF)
462 audio_stop();
463 #ifndef SIMULATOR
465 #if CONFIG_CODEC != SWCODEC
466 if(rec_create_directory() > 0)
467 have_recorded = true;
469 audio_init_recording(talk_get_bufsize());
471 sound_settings_apply();
472 /* Yes, we use the D/A for monitoring */
473 peak_meter_playback(true);
475 peak_meter_enabled = true;
477 rec_init_recording_options(&rec_options);
478 rec_options.rec_source = AUDIO_SRC_LINEIN;
479 rec_set_recording_options(&rec_options);
481 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
482 sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
484 #endif /* CONFIG_CODEC != SWCODEC */
485 #endif /* ndef SIMULATOR */
487 /* turn on radio */
488 #if CONFIG_CODEC == SWCODEC
489 rec_set_source(AUDIO_SRC_FMRADIO,
490 (radio_status == FMRADIO_PAUSED) ?
491 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
492 #else
493 if (radio_status == FMRADIO_OFF)
494 radio_start();
495 #endif
497 if(num_presets < 1 && yesno_pop(str(LANG_FM_FIRST_AUTOSCAN)))
498 scan_presets();
500 curr_preset = find_preset(curr_freq);
501 if(curr_preset != -1)
502 radio_mode = RADIO_PRESET_MODE;
504 #ifdef HAS_BUTTONBAR
505 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
506 str(LANG_FM_BUTTONBAR_PRESETS), str(LANG_FM_BUTTONBAR_RECORD));
507 #endif
509 #ifndef HAVE_NOISY_IDLE_MODE
510 cpu_idle_mode(true);
511 #endif
513 while(!done)
515 if(search_dir != 0)
517 curr_freq = step_freq(curr_freq, search_dir);
518 update_screen = true;
520 if(radio_set(RADIO_SCAN_FREQUENCY, curr_freq))
522 curr_preset = find_preset(curr_freq);
523 remember_frequency();
524 end_search();
527 trigger_cpu_boost();
530 #if CONFIG_CODEC != SWCODEC
531 /* TODO: Can we timeout at HZ when recording since peaks aren't
532 displayed? This should quiet recordings too. */
533 button = get_action(CONTEXT_FM,
534 update_screen ? TIMEOUT_NOBLOCK : HZ / PEAK_METER_FPS);
535 #else
536 button = get_action(CONTEXT_FM,
537 update_screen ? TIMEOUT_NOBLOCK : HZ);
538 #endif
540 #ifndef HAVE_NOISY_IDLE_MODE
541 if (button != ACTION_NONE)
543 cpu_idle_mode(false);
544 button_timeout = current_tick + (2*HZ);
546 #endif
547 switch(button)
549 case ACTION_FM_STOP:
550 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
551 if(audio_status() == AUDIO_STATUS_RECORD)
553 audio_stop();
555 else
556 #endif
558 done = true;
559 if(presets_changed)
561 if(yesno_pop(str(LANG_FM_SAVE_CHANGES)))
563 if(filepreset[0] == '\0')
564 save_preset_list();
565 else
566 radio_save_presets();
569 /* Clear the preset list on exit. */
570 clear_preset_list();
572 update_screen = true;
573 break;
575 #ifdef FM_RECORD
576 case ACTION_FM_RECORD:
577 #ifdef FM_RECORD_DBLPRE
578 if (lastbutton != ACTION_FM_RECORD_DBLPRE)
580 rec_lastclick = 0;
581 break;
583 if (current_tick - rec_lastclick > HZ/2)
585 rec_lastclick = current_tick;
586 break;
588 #endif /* FM_RECORD_DBLPRE */
589 #ifndef SIMULATOR
590 if(audio_status() == AUDIO_STATUS_RECORD)
592 rec_new_file();
593 update_screen = true;
595 else
597 have_recorded = true;
598 rec_record();
599 update_screen = true;
601 #endif /* SIMULATOR */
602 last_seconds = 0;
603 break;
604 #endif /* #ifdef FM_RECORD */
606 case ACTION_FM_EXIT:
607 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
608 if(audio_status() == AUDIO_STATUS_RECORD)
609 audio_stop();
610 #endif
611 keep_playing = true;
612 done = true;
613 ret_val = GO_TO_ROOT;
614 if(presets_changed)
616 if(yesno_pop(str(LANG_FM_SAVE_CHANGES)))
618 if(filepreset[0] == '\0')
619 save_preset_list();
620 else
621 radio_save_presets();
625 /* Clear the preset list on exit. */
626 clear_preset_list();
628 break;
630 case ACTION_STD_PREV:
631 case ACTION_STD_NEXT:
632 next_station(button == ACTION_STD_PREV ? -1 : 1);
633 end_search();
634 update_screen = true;
635 break;
637 case ACTION_STD_PREVREPEAT:
638 case ACTION_STD_NEXTREPEAT:
640 int dir = search_dir;
641 search_dir = button == ACTION_STD_PREVREPEAT ? -1 : 1;
642 if (radio_mode != RADIO_SCAN_MODE)
644 next_preset(search_dir);
645 end_search();
646 update_screen = true;
648 else if (dir == 0)
650 /* Starting auto scan */
651 radio_set(RADIO_MUTE, 1);
652 update_screen = true;
654 break;
657 case ACTION_SETTINGS_INC:
658 case ACTION_SETTINGS_INCREPEAT:
659 global_settings.volume++;
660 if(global_settings.volume > sound_max(SOUND_VOLUME))
661 global_settings.volume = sound_max(SOUND_VOLUME);
662 sound_set_volume(global_settings.volume);
663 update_screen = true;
664 settings_save();
665 break;
667 case ACTION_SETTINGS_DEC:
668 case ACTION_SETTINGS_DECREPEAT:
669 global_settings.volume--;
670 if(global_settings.volume < sound_min(SOUND_VOLUME))
671 global_settings.volume = sound_min(SOUND_VOLUME);
672 sound_set_volume(global_settings.volume);
673 update_screen = true;
674 settings_save();
675 break;
677 case ACTION_FM_PLAY:
678 if (radio_status == FMRADIO_PLAYING)
679 radio_pause();
680 else
681 radio_start();
683 update_screen = true;
684 break;
686 case ACTION_FM_MENU:
687 radio_menu();
688 curr_preset = find_preset(curr_freq);
689 FOR_NB_SCREENS(i){
690 struct screen *sc = &screens[i];
691 gui_textarea_clear(sc);
692 screen_set_xmargin(sc, 0);
694 #ifdef HAS_BUTTONBAR
695 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
696 str(LANG_FM_BUTTONBAR_PRESETS),
697 str(LANG_FM_BUTTONBAR_RECORD));
698 #endif
699 update_screen = true;
700 break;
702 #ifdef FM_PRESET
703 case ACTION_FM_PRESET:
704 if(num_presets < 1)
706 gui_syncsplash(HZ, str(LANG_FM_NO_PRESETS));
707 update_screen = true;
708 FOR_NB_SCREENS(i)
710 struct screen *sc = &screens[i];
711 gui_textarea_clear(sc);
712 screen_set_xmargin(sc, 0);
713 gui_textarea_update(sc);
716 break;
718 handle_radio_presets();
719 FOR_NB_SCREENS(i)
721 struct screen *sc = &screens[i];
722 gui_textarea_clear(sc);
723 screen_set_xmargin(sc, 0);
724 gui_textarea_update(sc);
726 #ifdef HAS_BUTTONBAR
727 gui_buttonbar_set(&buttonbar,
728 str(LANG_BUTTONBAR_MENU),
729 str(LANG_FM_BUTTONBAR_PRESETS),
730 str(LANG_FM_BUTTONBAR_RECORD));
731 #endif
732 update_screen = true;
733 break;
734 #endif /* FM_PRESET */
736 #ifdef FM_FREEZE
737 case ACTION_FM_FREEZE:
738 if(!screen_freeze)
740 gui_syncsplash(HZ, str(LANG_FM_FREEZE));
741 screen_freeze = true;
743 else
745 update_screen = true;
746 screen_freeze = false;
748 break;
749 #endif /* FM_FREEZE */
751 case SYS_USB_CONNECTED:
752 #if CONFIG_CODEC != SWCODEC
753 /* Only accept USB connection when not recording */
754 if(audio_status() != AUDIO_STATUS_RECORD)
755 #endif
757 default_event_handler(SYS_USB_CONNECTED);
758 screen_freeze = true; /* Cosmetic: makes sure the
759 radio screen doesn't redraw */
760 done = true;
762 break;
764 #ifdef FM_MODE
765 case ACTION_FM_MODE:
766 if(radio_mode == RADIO_SCAN_MODE)
768 /* Force scan mode if there are no presets. */
769 if(num_presets > 0)
770 radio_mode = RADIO_PRESET_MODE;
772 else
773 radio_mode = RADIO_SCAN_MODE;
774 update_screen = true;
775 break;
776 #endif /* FM_MODE */
778 #ifdef FM_NEXT_PRESET
779 case ACTION_FM_NEXT_PRESET:
780 next_preset(1);
781 end_search();
782 update_screen = true;
783 break;
784 #endif
786 #ifdef FM_PREV_PRESET
787 case ACTION_FM_PREV_PRESET:
788 next_preset(-1);
789 end_search();
790 update_screen = true;
791 break;
792 #endif
794 default:
795 default_event_handler(button);
796 break;
797 } /*switch(button)*/
799 #ifdef FM_RECORD_DBLPRE
800 if (button != ACTION_NONE)
801 lastbutton = button;
802 #endif
804 #if CONFIG_CODEC != SWCODEC
805 peak_meter_peek();
806 #endif
808 if(!screen_freeze)
810 /* Only display the peak meter when not recording */
811 #if CONFIG_CODEC != SWCODEC
812 if(!audio_status())
814 FOR_NB_SCREENS(i)
816 peak_meter_screen(&screens[i],0,
817 STATUSBAR_HEIGHT + fh*(top_of_screen + 4), fh);
818 screens[i].update_rect(0, STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
819 screens[i].width, fh);
823 if(TIME_AFTER(current_tick, timeout))
825 timeout = current_tick + HZ;
826 #else /* SWCODEC */
828 #endif /* CONFIG_CODEC == SWCODEC */
830 /* keep "mono" from always being displayed when paused */
831 if (radio_status != FMRADIO_PAUSED)
833 stereo = radio_get(RADIO_STEREO) &&
834 !global_settings.fm_force_mono;
836 if(stereo != last_stereo)
838 update_screen = true;
839 last_stereo = stereo;
844 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
845 seconds = audio_recorded_time() / HZ;
846 if (update_screen || seconds > last_seconds)
848 last_seconds = seconds;
849 #else
850 if (update_screen)
852 #endif
853 int freq;
855 FOR_NB_SCREENS(i)
856 screens[i].setfont(FONT_UI);
858 snprintf(buf, 128, curr_preset >= 0 ? "%d. %s" : " ",
859 curr_preset + 1, presets[curr_preset].name);
861 FOR_NB_SCREENS(i)
862 screens[i].puts_scroll(0, top_of_screen, buf);
864 freq = curr_freq / 10000;
865 snprintf(buf, 128, str(LANG_FM_STATION), freq / 100, freq % 100);
866 FOR_NB_SCREENS(i)
867 screens[i].puts_scroll(0, top_of_screen + 1, buf);
869 snprintf(buf, 128, stereo?str(LANG_CHANNEL_STEREO):
870 str(LANG_CHANNEL_MONO));
871 FOR_NB_SCREENS(i)
872 screens[i].puts_scroll(0, top_of_screen + 2, buf);
874 snprintf(buf, 128, "%s %s", str(LANG_FM_TUNE_MODE),
875 radio_mode ? str(LANG_RADIO_PRESET_MODE) :
876 str(LANG_RADIO_SCAN_MODE));
877 FOR_NB_SCREENS(i)
878 screens[i].puts_scroll(0, top_of_screen + 3, buf);
880 #if CONFIG_CODEC != SWCODEC
881 if(audio_status() == AUDIO_STATUS_RECORD)
883 hours = seconds / 3600;
884 minutes = (seconds - (hours * 3600)) / 60;
885 snprintf(buf, 32, "%s %02d:%02d:%02d",
886 str(LANG_RECORDING_TIME),
887 hours, minutes, seconds%60);
888 FOR_NB_SCREENS(i)
889 screens[i].puts_scroll(0, top_of_screen + 4, buf);
891 else
893 if(rec_options.rec_prerecord_time)
895 snprintf(buf, 32, "%s %02d",
896 str(LANG_RECORD_PRERECORD), seconds%60);
897 FOR_NB_SCREENS(i)
898 screens[i].puts_scroll(0, top_of_screen + 4, buf);
901 #endif /* CONFIG_CODEC != SWCODEC */
903 #ifdef HAS_BUTTONBAR
904 gui_buttonbar_draw(&buttonbar);
905 #endif
906 FOR_NB_SCREENS(i)
907 gui_textarea_update(&screens[i]);
909 /* Only force the redraw if update_screen is true */
910 gui_syncstatusbar_draw(&statusbars,true);
913 update_screen = false;
915 #if CONFIG_CODEC != SWCODEC
916 if(audio_status() & AUDIO_STATUS_ERROR)
918 done = true;
920 #endif
922 #ifndef HAVE_NOISY_IDLE_MODE
923 if (TIME_AFTER(current_tick, button_timeout))
925 cpu_idle_mode(true);
927 #endif
928 } /*while(!done)*/
930 #ifndef SIMULATOR
931 #if CONFIG_CODEC != SWCODEC
932 if(audio_status() & AUDIO_STATUS_ERROR)
934 gui_syncsplash(0, str(LANG_DISK_FULL));
935 gui_syncstatusbar_draw(&statusbars,true);
936 FOR_NB_SCREENS(i)
937 gui_textarea_update(&screens[i]);
938 audio_error_clear();
940 while(1)
942 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
943 if(button == ACTION_FM_STOP)
944 break;
948 audio_init_playback();
949 #endif /* CONFIG_CODEC != SWCODEC */
951 sound_settings_apply();
952 #endif /* SIMULATOR */
954 if(keep_playing)
956 /* Catch FMRADIO_PLAYING status for the sim. */
957 #ifndef SIMULATOR
958 #if CONFIG_CODEC != SWCODEC
959 /* Enable the Left and right A/D Converter */
960 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
961 sound_default(SOUND_RIGHT_GAIN),
962 AUDIO_GAIN_LINEIN);
963 mas_codec_writereg(6, 0x4000);
964 #endif
965 end_search();
966 #endif /* SIMULATOR */
968 else
970 #if CONFIG_CODEC == SWCODEC
971 rec_set_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
972 #else
973 radio_stop();
974 #endif
977 #ifndef HAVE_NOISY_IDLE_MODE
978 cpu_idle_mode(false);
979 #endif
981 /* restore status bar settings */
982 global_settings.statusbar = statusbar;
984 in_screen = false;
985 #if CONFIG_CODEC != SWCODEC
986 return have_recorded;
987 #else
988 return false;
989 #endif
990 } /* radio_screen */
992 static void radio_save_presets(void)
994 int fd;
995 int i;
997 fd = creat(filepreset);
998 if(fd >= 0)
1000 for(i = 0;i < num_presets;i++)
1002 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
1004 close(fd);
1006 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
1007 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
1008 presets_changed = false;
1010 else
1012 gui_syncsplash(HZ, str(LANG_FM_PRESET_SAVE_FAILED));
1016 void radio_load_presets(char *filename)
1018 int fd;
1019 int rc;
1020 char buf[128];
1021 char *freq;
1022 char *name;
1023 bool done = false;
1024 int f;
1026 memset(presets, 0, sizeof(presets));
1027 num_presets = 0;
1029 /* No Preset in configuration. */
1030 if(filename[0] == '\0')
1032 filepreset[0] = '\0';
1033 return;
1035 /* Temporary preset, loaded until player shuts down. */
1036 else if(filename[0] == '/')
1037 strncpy(filepreset, filename, sizeof(filepreset));
1038 /* Preset from default directory. */
1039 else
1040 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1041 FMPRESET_PATH, filename);
1043 fd = open(filepreset, O_RDONLY);
1044 if(fd >= 0)
1046 while(!done && num_presets < MAX_PRESETS)
1048 rc = read_line(fd, buf, 128);
1049 if(rc > 0)
1051 if(settings_parseline(buf, &freq, &name))
1053 f = atoi(freq);
1054 if(f) /* For backwards compatibility */
1056 struct fmstation * const fms = &presets[num_presets];
1057 fms->frequency = f;
1058 strncpy(fms->name, name, MAX_FMPRESET_LEN);
1059 fms->name[MAX_FMPRESET_LEN] = '\0';
1060 num_presets++;
1064 else
1065 done = true;
1067 close(fd);
1069 else /* invalid file name? */
1070 filepreset[0] = '\0';
1072 presets_loaded = num_presets > 0;
1073 presets_changed = false;
1077 static int radio_add_preset(void)
1079 char buf[MAX_FMPRESET_LEN];
1081 if(num_presets < MAX_PRESETS)
1083 memset(buf, 0, MAX_FMPRESET_LEN);
1085 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1087 struct fmstation * const fms = &presets[num_presets];
1088 buf[MAX_FMPRESET_LEN] = '\0';
1089 strcpy(fms->name, buf);
1090 fms->frequency = curr_freq;
1091 num_presets++;
1092 presets_changed = true;
1093 presets_loaded = num_presets > 0;
1096 else
1098 gui_syncsplash(HZ, str(LANG_FM_NO_FREE_PRESETS));
1100 return true;
1103 /* needed to know which preset we are edit/delete-ing */
1104 static int selected_preset = -1;
1105 static int radio_edit_preset(void)
1107 char buf[MAX_FMPRESET_LEN];
1109 if (num_presets > 0)
1111 struct fmstation * const fms = &presets[selected_preset];
1113 strncpy(buf, fms->name, MAX_FMPRESET_LEN);
1115 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1117 buf[MAX_FMPRESET_LEN] = '\0';
1118 strcpy(fms->name, buf);
1119 presets_changed = true;
1123 return 1;
1126 static int radio_delete_preset(void)
1128 if (num_presets > 0)
1130 struct fmstation * const fms = &presets[selected_preset];
1132 if (selected_preset >= --num_presets)
1133 selected_preset = num_presets - 1;
1135 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1136 (uintptr_t)fms);
1140 /* Don't ask to save when all presets are deleted. */
1141 presets_changed = num_presets > 0;
1143 if (!presets_changed)
1145 /* The preset list will be cleared, switch to Scan Mode. */
1146 radio_mode = RADIO_SCAN_MODE;
1147 presets_loaded = false;
1150 return 1;
1153 static int load_preset_list(void)
1155 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1158 static int save_preset_list(void)
1160 if(num_presets > 0)
1162 bool bad_file_name = true;
1164 if(!opendir(FMPRESET_PATH)) /* Check if there is preset folder */
1165 mkdir(FMPRESET_PATH);
1167 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1168 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1170 while(bad_file_name)
1172 if(!kbd_input(filepreset, sizeof(filepreset)))
1174 /* check the name: max MAX_FILENAME (20) chars */
1175 char* p2;
1176 char* p1;
1177 int len;
1178 p1 = strrchr(filepreset, '/');
1179 p2 = p1;
1180 while((p1) && (*p2) && (*p2 != '.'))
1181 p2++;
1182 len = (int)(p2-p1) - 1;
1183 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1185 /* no slash, too long or too short */
1186 gui_syncsplash(HZ, str(LANG_INVALID_FILENAME));
1188 else
1190 /* add correct extension (easier to always write)
1191 at this point, p2 points to 0 or the extension dot */
1192 *p2 = '\0';
1193 strcat(filepreset,".fmr");
1194 bad_file_name = false;
1195 radio_save_presets();
1198 else
1200 /* user aborted */
1201 return false;
1205 else
1206 gui_syncsplash(HZ, str(LANG_FM_NO_PRESETS));
1208 return true;
1211 static int clear_preset_list(void)
1213 /* Clear all the preset entries */
1214 memset(presets, 0, sizeof (presets));
1216 num_presets = 0;
1217 presets_loaded = false;
1218 /* The preset list will be cleared switch to Scan Mode. */
1219 radio_mode = RADIO_SCAN_MODE;
1221 presets_changed = false; /* Don't ask to save when clearing the list. */
1223 return true;
1226 MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1227 ID2P(LANG_FM_EDIT_PRESET),
1228 radio_edit_preset, NULL, NULL, Icon_NOICON);
1229 MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1230 ID2P(LANG_FM_DELETE_PRESET),
1231 radio_delete_preset, NULL, NULL, Icon_NOICON);
1232 int radio_preset_callback(int action, const struct menu_item_ex *this_item)
1234 if (action == ACTION_STD_OK)
1235 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1236 return action;
1237 (void)this_item;
1239 MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_FM_BUTTONBAR_PRESETS),
1240 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1241 &radio_delete_preset_item);
1242 /* present a list of preset stations */
1243 char * presets_get_name(int selected_item, void * data, char *buffer)
1245 (void)data;
1246 (void)buffer;
1247 return presets[selected_item].name;
1250 static int handle_radio_presets(void)
1252 struct gui_synclist lists;
1253 int result = 0;
1254 int action = ACTION_NONE;
1255 #ifdef HAS_BUTTONBAR
1256 struct gui_buttonbar buttonbar;
1257 #endif
1259 if(presets_loaded == false)
1260 return result;
1262 #ifdef HAS_BUTTONBAR
1263 gui_buttonbar_init(&buttonbar);
1264 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1265 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1266 str(LANG_FM_BUTTONBAR_EXIT),
1267 str(LANG_FM_BUTTONBAR_ACTION));
1268 gui_buttonbar_draw(&buttonbar);
1269 #endif
1270 gui_synclist_init(&lists, presets_get_name, NULL, false, 1);
1271 gui_synclist_set_title(&lists, str(LANG_FM_BUTTONBAR_PRESETS), NOICON);
1272 gui_synclist_set_icon_callback(&lists, NULL);
1273 gui_synclist_set_nb_items(&lists, num_presets);
1274 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1276 action_signalscreenchange();
1277 while (result == 0)
1279 gui_synclist_draw(&lists);
1280 gui_syncstatusbar_draw(&statusbars, true);
1281 action = get_action(CONTEXT_STD, HZ);
1283 gui_synclist_do_button(&lists, action, LIST_WRAP_UNLESS_HELD);
1284 switch (action)
1286 case ACTION_STD_MENU:
1287 radio_add_preset();
1288 break;
1289 case ACTION_STD_CANCEL:
1290 result = 1;
1291 break;
1292 case ACTION_STD_OK:
1293 curr_preset = gui_synclist_get_sel_pos(&lists);
1294 curr_freq = presets[curr_preset].frequency;
1295 next_station(0);
1296 remember_frequency();
1297 result = 1;
1298 break;
1299 case ACTION_F3:
1300 case ACTION_STD_CONTEXT:
1301 selected_preset = gui_synclist_get_sel_pos(&lists);
1302 do_menu(&handle_radio_preset_menu, NULL);
1303 break;
1304 default:
1305 if(default_event_handler(action) == SYS_USB_CONNECTED)
1306 result = 2;
1309 action_signalscreenchange();
1310 return result - 1;
1313 void toggle_mono_mode(bool mono)
1315 radio_set(RADIO_FORCE_MONO, mono);
1318 void set_radio_region(int region)
1320 #if (CONFIG_TUNER & TEA5767)
1321 radio_set(RADIO_SET_DEEMPHASIS,
1322 fm_region[region].deemphasis);
1323 radio_set(RADIO_SET_BAND, fm_region[region].band);
1324 #else
1325 (void)region;
1326 #endif
1327 next_station(0);
1328 remember_frequency();
1331 MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
1332 MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
1334 #ifndef FM_MODE
1335 char* get_mode_text(int selected_item, void * data, char *buffer)
1337 (void)selected_item;
1338 (void)data;
1339 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_FM_TUNE_MODE),
1340 radio_mode ? str(LANG_RADIO_PRESET_MODE) :
1341 str(LANG_RADIO_SCAN_MODE));
1342 return buffer;
1344 static int toggle_radio_mode(void)
1346 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
1347 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
1348 return 0;
1350 MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1351 toggle_radio_mode, NULL,
1352 get_mode_text, NULL, NULL, Icon_NOICON);
1353 #endif
1355 static int scan_presets(void)
1357 bool do_scan = true;
1359 if(num_presets > 0) /* Do that to avoid 2 questions. */
1360 do_scan = yesno_pop(str(LANG_FM_CLEAR_PRESETS));
1362 if(do_scan)
1364 const struct fm_region_setting * const fmr =
1365 &fm_region[global_settings.fm_region];
1366 char buf[MAX_FMPRESET_LEN];
1367 int i;
1369 curr_freq = fmr->freq_min;
1370 num_presets = 0;
1371 memset(presets, 0, sizeof(presets));
1372 radio_set(RADIO_MUTE, 1);
1374 while(curr_freq <= fmr->freq_max)
1376 int freq, frac;
1377 if (num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1378 break;
1380 freq = curr_freq / 10000;
1381 frac = freq % 100;
1382 freq /= 100;
1384 snprintf(buf, MAX_FMPRESET_LEN, str(LANG_FM_SCANNING), freq, frac);
1385 gui_syncsplash(0, buf);
1387 if(radio_set(RADIO_SCAN_FREQUENCY, curr_freq))
1389 /* add preset */
1390 snprintf(buf, MAX_FMPRESET_LEN,
1391 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1392 strcpy(presets[num_presets].name,buf);
1393 presets[num_presets].frequency = curr_freq;
1394 num_presets++;
1397 curr_freq += fmr->freq_step;
1400 if (radio_status == FMRADIO_PLAYING)
1401 radio_set(RADIO_MUTE, 0);
1403 presets_changed = true;
1405 FOR_NB_SCREENS(i)
1407 gui_textarea_clear(&screens[i]);
1408 screen_set_xmargin(&screens[i],0);
1409 gui_textarea_update(&screens[i]);
1412 if(num_presets > 0)
1414 curr_freq = presets[0].frequency;
1415 radio_mode = RADIO_PRESET_MODE;
1416 presets_loaded = true;
1417 next_station(0);
1419 else
1421 /* Wrap it to beginning or we'll be past end of band */
1422 presets_loaded = false;
1423 next_station(1);
1426 return true;
1430 #ifdef HAVE_RECORDING
1432 #if defined(HAVE_FMRADIO_IN) && CONFIG_CODEC == SWCODEC
1433 #define FM_RECORDING_SCREEN
1434 static int fm_recording_screen(void)
1436 bool ret;
1438 /* switch recording source to FMRADIO for the duration */
1439 int rec_source = global_settings.rec_source;
1440 global_settings.rec_source = AUDIO_SRC_FMRADIO;
1442 /* clearing queue seems to cure a spontaneous abort during record */
1443 action_signalscreenchange();
1445 ret = recording_screen(true);
1447 /* safe to reset as changing sources is prohibited here */
1448 global_settings.rec_source = rec_source;
1450 return ret;
1453 #endif /* defined(HAVE_FMRADIO_IN) && CONFIG_CODEC == SWCODEC */
1455 #if defined(HAVE_FMRADIO_IN) || CONFIG_CODEC != SWCODEC
1456 #define FM_RECORDING_SETTINGS
1457 static int fm_recording_settings(void)
1459 bool ret = recording_menu(true);
1461 #if CONFIG_CODEC != SWCODEC
1462 if (!ret)
1464 struct audio_recording_options rec_options;
1465 rec_init_recording_options(&rec_options);
1466 rec_options.rec_source = AUDIO_SRC_LINEIN;
1467 rec_set_recording_options(&rec_options);
1469 #endif
1471 return ret;
1474 #endif /* defined(HAVE_FMRADIO_IN) || CONFIG_CODEC != SWCODEC */
1475 #endif /* HAVE_RECORDING */
1477 #ifdef FM_RECORDING_SCREEN
1478 MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING_MENU),
1479 fm_recording_screen, NULL, NULL, Icon_NOICON);
1480 #endif
1481 #ifdef FM_RECORDING_SETTINGS
1482 MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1483 fm_recording_settings, NULL, NULL, Icon_NOICON);
1484 #endif
1485 #ifndef FM_PRESET
1486 MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_FM_BUTTONBAR_PRESETS),
1487 handle_radio_presets, NULL, NULL, Icon_NOICON);
1488 #endif
1489 #ifndef FM_PRESET_ADD
1490 MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1491 radio_add_preset, NULL, NULL, Icon_NOICON);
1492 #endif
1495 MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1496 load_preset_list, NULL, NULL, Icon_NOICON);
1497 MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1498 save_preset_list, NULL, NULL, Icon_NOICON);
1499 MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1500 clear_preset_list, NULL, NULL, Icon_NOICON);
1501 MENUITEM_FUNCTION(scan_presets_item, 0, ID2P(LANG_FM_SCAN_PRESETS),
1502 scan_presets, NULL, NULL, Icon_NOICON);
1504 MAKE_MENU(radio_menu_items, ID2P(LANG_FM_MENU), NULL,
1505 Icon_Radio_screen,
1506 #ifndef FM_PRESET
1507 &radio_presets_item,
1508 #endif
1509 #ifndef FM_PRESET_ADD
1510 &radio_addpreset_item,
1511 #endif
1512 &presetload_item, &presetsave_item, &presetclear_item,
1513 &force_mono,
1514 #ifndef FM_MODE
1515 &radio_mode_item,
1516 #endif
1517 &set_region, &sound_settings,
1518 #ifdef FM_RECORDING_SCREEN
1519 &recscreen_item,
1520 #endif
1521 #ifdef FM_RECORDING_SETTINGS
1522 &recsettings_item,
1523 #endif
1524 &scan_presets_item);
1525 /* main menu of the radio screen */
1526 static bool radio_menu(void)
1528 return do_menu(&radio_menu_items, NULL) == MENU_ATTACHED_USB;
1531 #endif