Refine meg-fx pad changes. Actually watch the other <dir>+ lines and ignore them...
[kugel-rb.git] / apps / recorder / radio.c
blob38319dca9e3e63ca91870882bd5c07752d568b72
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 "string.h"
37 #include "system.h"
38 #include "radio.h"
39 #include "menu.h"
40 #include "misc.h"
41 #include "keyboard.h"
42 #include "screens.h"
43 #include "peakmeter.h"
44 #include "lang.h"
45 #include "font.h"
46 #include "sound_menu.h"
47 #ifdef HAVE_RECORDING
48 #include "recording.h"
49 #endif
50 #include "talk.h"
51 #include "tuner.h"
52 #include "power.h"
53 #include "sound.h"
54 #include "screen_access.h"
55 #include "statusbar.h"
56 #include "textarea.h"
57 #include "splash.h"
58 #include "yesno.h"
59 #include "buttonbar.h"
60 #include "power.h"
61 #include "tree.h"
62 #include "dir.h"
63 #include "action.h"
64 #include "list.h"
65 #include "menus/exported_menus.h"
66 #include "root_menu.h"
68 #if CONFIG_TUNER
70 #if CONFIG_KEYPAD == RECORDER_PAD
71 #define FM_RECORD
72 #define FM_PRESET_ADD
73 #define FM_PRESET_ACTION
74 #define FM_PRESET
75 #define FM_MODE
77 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
78 #define FM_PRESET
79 #define FM_MODE
80 #define FM_NEXT_PRESET
81 #define FM_PREV_PRESET
83 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
84 #define FM_PRESET
85 #define FM_MODE
87 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
88 #define FM_PRESET
89 #define FM_MODE
90 /* This should be removeable if the whole tuning thing is sorted out since
91 proper tuning quiets the screen almost entirely in that extreme measures
92 have to be taken to hear any interference. */
93 #define HAVE_NOISY_IDLE_MODE
95 #elif CONFIG_KEYPAD == ONDIO_PAD
96 #define FM_RECORD_DBLPRE
97 #define FM_RECORD
98 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || (CONFIG_KEYPAD == SANSA_C200_PAD)
99 #define FM_MENU
100 #define FM_PRESET
101 #define FM_STOP
102 #define FM_MODE
103 #define FM_EXIT
104 #define FM_PLAY
105 #endif
107 #define RADIO_SCAN_MODE 0
108 #define RADIO_PRESET_MODE 1
110 static int curr_preset = -1;
111 static int curr_freq;
112 static int radio_mode = RADIO_SCAN_MODE;
113 static int search_dir = 0;
115 static int radio_status = FMRADIO_OFF;
116 static bool in_screen = false;
118 #define MAX_PRESETS 64
119 static bool presets_loaded = false, presets_changed = false;
120 static struct fmstation presets[MAX_PRESETS];
122 static char filepreset[MAX_PATH]; /* preset filename variable */
124 static int num_presets = 0; /* The number of presets in the preset list */
126 static void radio_save_presets(void);
127 static int handle_radio_presets(void);
128 static bool radio_menu(void);
129 static int radio_add_preset(void);
130 static int save_preset_list(void);
131 static int load_preset_list(void);
132 static int clear_preset_list(void);
134 static int scan_presets(void);
136 /* Function to manipulate all yesno dialogues.
137 This function needs the output text as an argument. */
138 static bool yesno_pop(const char* text)
140 int i;
141 const char *lines[]={text};
142 const struct text_message message={lines, 1};
143 bool ret = (gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
144 FOR_NB_SCREENS(i)
145 gui_textarea_clear(&screens[i]);
146 return ret;
149 void radio_init(void)
151 tuner_init();
152 radio_stop();
155 int get_radio_status(void)
157 return radio_status;
160 bool in_radio_screen(void)
162 return in_screen;
165 /* TODO: Move some more of the control functionality to an HAL and clean up the
166 mess */
168 /* secret flag for starting paused - prevents unmute */
169 #define FMRADIO_START_PAUSED 0x8000
170 void radio_start(void)
172 const struct fm_region_data *fmr;
173 bool start_paused;
175 if(radio_status == FMRADIO_PLAYING)
176 return;
178 fmr = &fm_region_data[global_settings.fm_region];
180 start_paused = radio_status & FMRADIO_START_PAUSED;
181 /* clear flag before any yielding */
182 radio_status &= ~FMRADIO_START_PAUSED;
184 if(radio_status == FMRADIO_OFF)
185 tuner_power(true);
187 curr_freq = global_status.last_frequency
188 * fmr->freq_step + fmr->freq_min;
190 tuner_set(RADIO_SLEEP, 0); /* wake up the tuner */
192 if(radio_status == FMRADIO_OFF)
194 #ifdef HAVE_RADIO_REGION
195 tuner_set(RADIO_REGION, global_settings.fm_region);
196 #endif
197 tuner_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
200 tuner_set(RADIO_FREQUENCY, curr_freq);
202 #ifdef HAVE_RADIO_MUTE_TIMEOUT
204 unsigned long mute_timeout = current_tick + HZ;
205 if (radio_status != FMRADIO_OFF)
207 /* paused */
208 mute_timeout += HZ;
211 while(!tuner_get(RADIO_STEREO) && !tuner_get(RADIO_TUNED))
213 if(TIME_AFTER(current_tick, mute_timeout))
214 break;
215 yield();
218 #endif
220 /* keep radio from sounding initially */
221 if(!start_paused)
222 tuner_set(RADIO_MUTE, 0);
224 radio_status = FMRADIO_PLAYING;
225 } /* radio_start */
227 void radio_pause(void)
229 if(radio_status == FMRADIO_PAUSED)
230 return;
232 if(radio_status == FMRADIO_OFF)
234 radio_status |= FMRADIO_START_PAUSED;
235 radio_start();
238 tuner_set(RADIO_MUTE, 1);
239 tuner_set(RADIO_SLEEP, 1);
241 radio_status = FMRADIO_PAUSED;
242 } /* radio_pause */
244 void radio_stop(void)
246 if(radio_status == FMRADIO_OFF)
247 return;
249 tuner_set(RADIO_MUTE, 1);
250 tuner_set(RADIO_SLEEP, 1); /* low power mode, if available */
251 radio_status = FMRADIO_OFF;
252 tuner_power(false); /* status update, power off if avail. */
253 } /* radio_stop */
255 bool radio_hardware_present(void)
257 return tuner_get(RADIO_PRESENT);
260 /* Keep freq on the grid for the current region */
261 static int snap_freq_to_grid(int freq)
263 const struct fm_region_data * const fmr =
264 &fm_region_data[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_data * const fmr =
326 &fm_region_data[global_settings.fm_region];
327 global_status.last_frequency = (curr_freq - fmr->freq_min)
328 / fmr->freq_step;
329 status_save();
332 static void next_preset(int direction)
334 if (num_presets < 1)
335 return;
337 if (curr_preset == -1)
338 curr_preset = find_closest_preset(curr_freq, direction);
339 else
340 curr_preset = (curr_preset + direction + num_presets) % num_presets;
342 /* Must stay on the current grid for the region */
343 curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
345 tuner_set(RADIO_FREQUENCY, curr_freq);
346 remember_frequency();
349 /* Step to the next or previous frequency */
350 static int step_freq(int freq, int direction)
352 const struct fm_region_data * const fmr =
353 &fm_region_data[global_settings.fm_region];
355 freq += direction*fmr->freq_step;
357 /* Wrap first or snapping to grid will not let us on the band extremes */
358 if (freq > fmr->freq_max)
359 freq = direction > 0 ? fmr->freq_min : fmr->freq_max;
360 else if (freq < fmr->freq_min)
361 freq = direction < 0 ? fmr->freq_max : fmr->freq_min;
362 else
363 freq = snap_freq_to_grid(freq);
365 return freq;
368 /* Step to the next or previous station */
369 static void next_station(int direction)
371 if (direction != 0 && radio_mode != RADIO_SCAN_MODE)
373 next_preset(direction);
374 return;
377 curr_freq = step_freq(curr_freq, direction);
379 if (radio_status == FMRADIO_PLAYING)
380 tuner_set(RADIO_MUTE, 1);
382 tuner_set(RADIO_FREQUENCY, curr_freq);
384 if (radio_status == FMRADIO_PLAYING)
385 tuner_set(RADIO_MUTE, 0);
387 curr_preset = find_preset(curr_freq);
388 remember_frequency();
391 /* Ends an in-progress search */
392 static void end_search(void)
394 if (search_dir != 0 && radio_status == FMRADIO_PLAYING)
395 tuner_set(RADIO_MUTE, 0);
396 search_dir = 0;
399 /* Speak a frequency. */
400 static void talk_freq(int freq, bool enqueue)
402 freq /= 10000;
403 talk_number(freq / 100, enqueue);
404 talk_id(LANG_POINT, true);
405 talk_number(freq % 100 / 10, true);
406 if (freq % 10)
407 talk_number(freq % 10, true);
410 /* Speak a preset by number or by spelling its name, depending on settings. */
411 static void talk_preset(int preset, bool fallback, bool enqueue)
413 if (global_settings.talk_file == 1) /* number */
414 talk_number(preset + 1, enqueue);
415 else
416 { /* spell */
417 if(presets[preset].name[0])
418 talk_spell(presets[preset].name, enqueue);
419 else if(fallback)
420 talk_freq(presets[preset].frequency, enqueue);
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 bool talk = false;
439 #ifdef FM_RECORD_DBLPRE
440 int lastbutton = BUTTON_NONE;
441 unsigned long rec_lastclick = 0;
442 #endif
443 #if CONFIG_CODEC != SWCODEC
444 bool have_recorded = false;
445 int timeout = current_tick + HZ/10;
446 unsigned int seconds = 0;
447 unsigned int last_seconds = 0;
448 int hours, minutes;
449 struct audio_recording_options rec_options;
450 #endif /* CONFIG_CODEC != SWCODEC */
451 #ifndef HAVE_NOISY_IDLE_MODE
452 int button_timeout = current_tick + (2*HZ);
453 #endif
454 #ifdef HAS_BUTTONBAR
455 struct gui_buttonbar buttonbar;
456 gui_buttonbar_init(&buttonbar);
457 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
458 #endif
460 /* change status to "in screen" */
461 in_screen = true;
463 /* always display status bar in radio screen for now */
464 global_status.statusbar_forced = statusbar?0:1;
465 global_settings.statusbar = true;
466 FOR_NB_SCREENS(i)
468 gui_textarea_clear(&screens[i]);
469 screen_set_xmargin(&screens[i],0);
472 gui_syncstatusbar_draw(&statusbars,true);
474 fh = font_get(FONT_UI)->height;
476 /* Adjust for font size, trying to center the information vertically */
477 if(fh < 10)
478 top_of_screen = 1;
480 if(num_presets <= 0)
482 memset(presets, 0, sizeof(presets));
483 radio_load_presets(global_settings.fmr_file);
486 if(radio_status == FMRADIO_OFF)
487 audio_stop();
488 #ifndef SIMULATOR
490 #if CONFIG_CODEC != SWCODEC
491 if(rec_create_directory() > 0)
492 have_recorded = true;
494 audio_init_recording(talk_get_bufsize());
496 sound_settings_apply();
497 /* Yes, we use the D/A for monitoring */
498 peak_meter_playback(true);
500 peak_meter_enabled = true;
502 rec_init_recording_options(&rec_options);
503 rec_options.rec_source = AUDIO_SRC_LINEIN;
504 rec_set_recording_options(&rec_options);
506 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
507 sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
509 #endif /* CONFIG_CODEC != SWCODEC */
510 #endif /* ndef SIMULATOR */
512 /* turn on radio */
513 #if CONFIG_CODEC == SWCODEC
514 audio_set_input_source(AUDIO_SRC_FMRADIO,
515 (radio_status == FMRADIO_PAUSED) ?
516 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
517 #else
518 if (radio_status == FMRADIO_OFF)
519 radio_start();
520 #endif
522 if(num_presets < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN)))
523 scan_presets();
525 curr_preset = find_preset(curr_freq);
526 if(curr_preset != -1)
527 radio_mode = RADIO_PRESET_MODE;
529 #ifdef HAS_BUTTONBAR
530 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
531 str(LANG_PRESET), str(LANG_FM_BUTTONBAR_RECORD));
532 #endif
534 #ifndef HAVE_NOISY_IDLE_MODE
535 cpu_idle_mode(true);
536 #endif
538 while(!done)
540 if(search_dir != 0)
542 curr_freq = step_freq(curr_freq, search_dir);
543 update_screen = true;
545 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
547 curr_preset = find_preset(curr_freq);
548 remember_frequency();
549 end_search();
550 talk = true;
553 trigger_cpu_boost();
556 if (!update_screen)
558 cancel_cpu_boost();
561 #if CONFIG_CODEC != SWCODEC
562 /* TODO: Can we timeout at HZ when recording since peaks aren't
563 displayed? This should quiet recordings too. */
564 button = get_action(CONTEXT_FM,
565 update_screen ? TIMEOUT_NOBLOCK : HZ / PEAK_METER_FPS);
566 #else
567 button = get_action(CONTEXT_FM,
568 update_screen ? TIMEOUT_NOBLOCK : HZ);
569 #endif
571 #ifndef HAVE_NOISY_IDLE_MODE
572 if (button != ACTION_NONE)
574 cpu_idle_mode(false);
575 button_timeout = current_tick + (2*HZ);
577 #endif
578 switch(button)
580 case ACTION_FM_STOP:
581 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
582 if(audio_status() == AUDIO_STATUS_RECORD)
584 audio_stop();
586 else
587 #endif
589 done = true;
590 if(presets_changed)
592 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
594 if(filepreset[0] == '\0')
595 save_preset_list();
596 else
597 radio_save_presets();
600 /* Clear the preset list on exit. */
601 clear_preset_list();
603 update_screen = true;
604 break;
606 #ifdef FM_RECORD
607 case ACTION_FM_RECORD:
608 #ifdef FM_RECORD_DBLPRE
609 if (lastbutton != ACTION_FM_RECORD_DBLPRE)
611 rec_lastclick = 0;
612 break;
614 if (current_tick - rec_lastclick > HZ/2)
616 rec_lastclick = current_tick;
617 break;
619 #endif /* FM_RECORD_DBLPRE */
620 #ifndef SIMULATOR
621 if(audio_status() == AUDIO_STATUS_RECORD)
623 rec_command(RECORDING_CMD_START_NEWFILE);
624 update_screen = true;
626 else
628 have_recorded = true;
629 rec_command(RECORDING_CMD_START);
630 update_screen = true;
632 #endif /* SIMULATOR */
633 last_seconds = 0;
634 break;
635 #endif /* #ifdef FM_RECORD */
637 case ACTION_FM_EXIT:
638 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
639 if(audio_status() == AUDIO_STATUS_RECORD)
640 audio_stop();
641 #endif
642 keep_playing = true;
643 done = true;
644 ret_val = GO_TO_ROOT;
645 if(presets_changed)
647 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
649 if(filepreset[0] == '\0')
650 save_preset_list();
651 else
652 radio_save_presets();
656 /* Clear the preset list on exit. */
657 clear_preset_list();
659 break;
661 case ACTION_STD_PREV:
662 case ACTION_STD_NEXT:
663 next_station(button == ACTION_STD_PREV ? -1 : 1);
664 end_search();
665 update_screen = true;
666 talk = true;
667 break;
669 case ACTION_STD_PREVREPEAT:
670 case ACTION_STD_NEXTREPEAT:
672 int dir = search_dir;
673 search_dir = button == ACTION_STD_PREVREPEAT ? -1 : 1;
674 if (radio_mode != RADIO_SCAN_MODE)
676 next_preset(search_dir);
677 end_search();
678 update_screen = true;
679 talk = true;
681 else if (dir == 0)
683 /* Starting auto scan */
684 tuner_set(RADIO_MUTE, 1);
685 update_screen = true;
687 break;
690 case ACTION_SETTINGS_INC:
691 case ACTION_SETTINGS_INCREPEAT:
692 global_settings.volume++;
693 setvol();
694 update_screen = true;
695 break;
697 case ACTION_SETTINGS_DEC:
698 case ACTION_SETTINGS_DECREPEAT:
699 global_settings.volume--;
700 setvol();
701 update_screen = true;
702 break;
704 case ACTION_FM_PLAY:
705 if (radio_status == FMRADIO_PLAYING)
706 radio_pause();
707 else
708 radio_start();
710 update_screen = true;
711 talk = false;
712 talk_shutup();
713 break;
715 case ACTION_FM_MENU:
716 radio_menu();
717 curr_preset = find_preset(curr_freq);
718 FOR_NB_SCREENS(i){
719 struct screen *sc = &screens[i];
720 gui_textarea_clear(sc);
721 screen_set_xmargin(sc, 0);
723 #ifdef HAS_BUTTONBAR
724 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
725 str(LANG_PRESET),
726 str(LANG_FM_BUTTONBAR_RECORD));
727 #endif
728 update_screen = true;
729 break;
731 #ifdef FM_PRESET
732 case ACTION_FM_PRESET:
733 if(num_presets < 1)
735 gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS));
736 update_screen = true;
737 FOR_NB_SCREENS(i)
739 struct screen *sc = &screens[i];
740 gui_textarea_clear(sc);
741 screen_set_xmargin(sc, 0);
742 gui_textarea_update(sc);
745 break;
747 handle_radio_presets();
748 FOR_NB_SCREENS(i)
750 struct screen *sc = &screens[i];
751 gui_textarea_clear(sc);
752 screen_set_xmargin(sc, 0);
753 gui_textarea_update(sc);
755 #ifdef HAS_BUTTONBAR
756 gui_buttonbar_set(&buttonbar,
757 str(LANG_BUTTONBAR_MENU),
758 str(LANG_PRESET),
759 str(LANG_FM_BUTTONBAR_RECORD));
760 #endif
761 update_screen = true;
762 break;
763 #endif /* FM_PRESET */
765 #ifdef FM_FREEZE
766 case ACTION_FM_FREEZE:
767 if(!screen_freeze)
769 gui_syncsplash(HZ, str(LANG_FM_FREEZE));
770 screen_freeze = true;
772 else
774 update_screen = true;
775 screen_freeze = false;
777 break;
778 #endif /* FM_FREEZE */
780 case SYS_USB_CONNECTED:
781 #if CONFIG_CODEC != SWCODEC
782 /* Only accept USB connection when not recording */
783 if(audio_status() != AUDIO_STATUS_RECORD)
784 #endif
786 default_event_handler(SYS_USB_CONNECTED);
787 screen_freeze = true; /* Cosmetic: makes sure the
788 radio screen doesn't redraw */
789 done = true;
791 break;
793 #ifdef FM_MODE
794 case ACTION_FM_MODE:
795 if(radio_mode == RADIO_SCAN_MODE)
797 /* Force scan mode if there are no presets. */
798 if(num_presets > 0)
799 radio_mode = RADIO_PRESET_MODE;
801 else
802 radio_mode = RADIO_SCAN_MODE;
803 update_screen = true;
804 cond_talk_ids_fq(radio_mode ?
805 LANG_PRESET : LANG_RADIO_SCAN_MODE);
806 talk = true;
807 break;
808 #endif /* FM_MODE */
810 #ifdef FM_NEXT_PRESET
811 case ACTION_FM_NEXT_PRESET:
812 next_preset(1);
813 end_search();
814 update_screen = true;
815 talk = true;
816 break;
817 #endif
819 #ifdef FM_PREV_PRESET
820 case ACTION_FM_PREV_PRESET:
821 next_preset(-1);
822 end_search();
823 update_screen = true;
824 talk = true;
825 break;
826 #endif
828 default:
829 default_event_handler(button);
830 break;
831 } /*switch(button)*/
833 #ifdef FM_RECORD_DBLPRE
834 if (button != ACTION_NONE)
835 lastbutton = button;
836 #endif
838 #if CONFIG_CODEC != SWCODEC
839 peak_meter_peek();
840 #endif
842 if(!screen_freeze)
844 /* Only display the peak meter when not recording */
845 #if CONFIG_CODEC != SWCODEC
846 if(!audio_status())
848 FOR_NB_SCREENS(i)
850 peak_meter_screen(&screens[i],0,
851 STATUSBAR_HEIGHT + fh*(top_of_screen + 4), fh);
852 screens[i].update_rect(0, STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
853 screens[i].width, fh);
857 if(TIME_AFTER(current_tick, timeout))
859 timeout = current_tick + HZ;
860 #else /* SWCODEC */
862 #endif /* CONFIG_CODEC == SWCODEC */
864 /* keep "mono" from always being displayed when paused */
865 if (radio_status != FMRADIO_PAUSED)
867 stereo = tuner_get(RADIO_STEREO) &&
868 !global_settings.fm_force_mono;
870 if(stereo != last_stereo)
872 update_screen = true;
873 last_stereo = stereo;
878 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
879 seconds = audio_recorded_time() / HZ;
880 if (update_screen || seconds > last_seconds)
882 last_seconds = seconds;
883 #else
884 if (update_screen)
886 #endif
887 int freq;
889 FOR_NB_SCREENS(i)
890 screens[i].setfont(FONT_UI);
892 snprintf(buf, 128, curr_preset >= 0 ? "%d. %s" : " ",
893 curr_preset + 1, presets[curr_preset].name);
895 FOR_NB_SCREENS(i)
896 screens[i].puts_scroll(0, top_of_screen, buf);
898 freq = curr_freq / 10000;
899 snprintf(buf, 128, str(LANG_FM_STATION), freq / 100, freq % 100);
900 FOR_NB_SCREENS(i)
901 screens[i].puts_scroll(0, top_of_screen + 1, buf);
903 snprintf(buf, 128, stereo?str(LANG_CHANNEL_STEREO):
904 str(LANG_CHANNEL_MONO));
905 FOR_NB_SCREENS(i)
906 screens[i].puts_scroll(0, top_of_screen + 2, buf);
908 snprintf(buf, 128, "%s %s", str(LANG_MODE),
909 radio_mode ? str(LANG_PRESET) :
910 str(LANG_RADIO_SCAN_MODE));
911 FOR_NB_SCREENS(i)
912 screens[i].puts_scroll(0, top_of_screen + 3, buf);
914 #if CONFIG_CODEC != SWCODEC
915 if(audio_status() == AUDIO_STATUS_RECORD)
917 hours = seconds / 3600;
918 minutes = (seconds - (hours * 3600)) / 60;
919 snprintf(buf, 32, "%s %02d:%02d:%02d",
920 str(LANG_RECORDING_TIME),
921 hours, minutes, seconds%60);
922 FOR_NB_SCREENS(i)
923 screens[i].puts_scroll(0, top_of_screen + 4, buf);
925 else
927 if(rec_options.rec_prerecord_time)
929 snprintf(buf, 32, "%s %02d",
930 str(LANG_RECORD_PRERECORD), seconds%60);
931 FOR_NB_SCREENS(i)
932 screens[i].puts_scroll(0, top_of_screen + 4, buf);
935 #endif /* CONFIG_CODEC != SWCODEC */
937 #ifdef HAS_BUTTONBAR
938 gui_buttonbar_draw(&buttonbar);
939 #endif
940 FOR_NB_SCREENS(i)
941 gui_textarea_update(&screens[i]);
943 /* Only force the redraw if update_screen is true */
944 gui_syncstatusbar_draw(&statusbars,true);
947 update_screen = false;
949 if (global_settings.talk_file && talk
950 && radio_status == FMRADIO_PAUSED)
952 talk = false;
953 bool enqueue = false;
954 if (radio_mode == RADIO_SCAN_MODE)
956 talk_freq(curr_freq, enqueue);
957 enqueue = true;
959 if (curr_preset >= 0)
960 talk_preset(curr_preset, radio_mode == RADIO_PRESET_MODE,
961 enqueue);
964 #if CONFIG_CODEC != SWCODEC
965 if(audio_status() & AUDIO_STATUS_ERROR)
967 done = true;
969 #endif
971 #ifndef HAVE_NOISY_IDLE_MODE
972 if (TIME_AFTER(current_tick, button_timeout))
974 cpu_idle_mode(true);
976 #endif
977 } /*while(!done)*/
979 #ifndef SIMULATOR
980 #if CONFIG_CODEC != SWCODEC
981 if(audio_status() & AUDIO_STATUS_ERROR)
983 gui_syncsplash(0, str(LANG_DISK_FULL));
984 gui_syncstatusbar_draw(&statusbars,true);
985 FOR_NB_SCREENS(i)
986 gui_textarea_update(&screens[i]);
987 audio_error_clear();
989 while(1)
991 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
992 if(button == ACTION_FM_STOP)
993 break;
997 audio_init_playback();
998 #endif /* CONFIG_CODEC != SWCODEC */
1000 sound_settings_apply();
1001 #endif /* SIMULATOR */
1003 if(keep_playing)
1005 /* Catch FMRADIO_PLAYING status for the sim. */
1006 #ifndef SIMULATOR
1007 #if CONFIG_CODEC != SWCODEC
1008 /* Enable the Left and right A/D Converter */
1009 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
1010 sound_default(SOUND_RIGHT_GAIN),
1011 AUDIO_GAIN_LINEIN);
1012 mas_codec_writereg(6, 0x4000);
1013 #endif
1014 end_search();
1015 #endif /* SIMULATOR */
1017 else
1019 #if CONFIG_CODEC == SWCODEC
1020 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
1021 #else
1022 radio_stop();
1023 #endif
1026 #ifndef HAVE_NOISY_IDLE_MODE
1027 cpu_idle_mode(false);
1028 #endif
1030 /* restore status bar settings */
1031 global_settings.statusbar = statusbar;
1032 global_status.statusbar_forced = 0;
1033 in_screen = false;
1034 #if CONFIG_CODEC != SWCODEC
1035 return have_recorded;
1036 #else
1037 return false;
1038 #endif
1039 } /* radio_screen */
1041 static void radio_save_presets(void)
1043 int fd;
1044 int i;
1046 fd = creat(filepreset);
1047 if(fd >= 0)
1049 for(i = 0;i < num_presets;i++)
1051 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
1053 close(fd);
1055 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
1056 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
1057 presets_changed = false;
1059 else
1061 gui_syncsplash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED));
1065 void radio_load_presets(char *filename)
1067 int fd;
1068 int rc;
1069 char buf[128];
1070 char *freq;
1071 char *name;
1072 bool done = false;
1073 int f;
1075 memset(presets, 0, sizeof(presets));
1076 num_presets = 0;
1078 /* No Preset in configuration. */
1079 if(filename[0] == '\0')
1081 filepreset[0] = '\0';
1082 return;
1084 /* Temporary preset, loaded until player shuts down. */
1085 else if(filename[0] == '/')
1086 strncpy(filepreset, filename, sizeof(filepreset));
1087 /* Preset from default directory. */
1088 else
1089 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1090 FMPRESET_PATH, filename);
1092 fd = open(filepreset, O_RDONLY);
1093 if(fd >= 0)
1095 while(!done && num_presets < MAX_PRESETS)
1097 rc = read_line(fd, buf, 128);
1098 if(rc > 0)
1100 if(settings_parseline(buf, &freq, &name))
1102 f = atoi(freq);
1103 if(f) /* For backwards compatibility */
1105 struct fmstation * const fms = &presets[num_presets];
1106 fms->frequency = f;
1107 strncpy(fms->name, name, MAX_FMPRESET_LEN);
1108 fms->name[MAX_FMPRESET_LEN] = '\0';
1109 num_presets++;
1113 else
1114 done = true;
1116 close(fd);
1118 else /* invalid file name? */
1119 filepreset[0] = '\0';
1121 presets_loaded = num_presets > 0;
1122 presets_changed = false;
1126 static int radio_add_preset(void)
1128 char buf[MAX_FMPRESET_LEN + 1];
1130 if(num_presets < MAX_PRESETS)
1132 memset(buf, 0, MAX_FMPRESET_LEN);
1134 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1136 struct fmstation * const fms = &presets[num_presets];
1137 buf[MAX_FMPRESET_LEN] = '\0';
1138 strcpy(fms->name, buf);
1139 fms->frequency = curr_freq;
1140 num_presets++;
1141 presets_changed = true;
1142 presets_loaded = num_presets > 0;
1145 else
1147 gui_syncsplash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS));
1149 return true;
1152 /* needed to know which preset we are edit/delete-ing */
1153 static int selected_preset = -1;
1154 static int radio_edit_preset(void)
1156 char buf[MAX_FMPRESET_LEN + 1];
1158 if (num_presets > 0)
1160 struct fmstation * const fms = &presets[selected_preset];
1162 strncpy(buf, fms->name, MAX_FMPRESET_LEN);
1164 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1166 buf[MAX_FMPRESET_LEN] = '\0';
1167 strcpy(fms->name, buf);
1168 presets_changed = true;
1172 return 1;
1175 static int radio_delete_preset(void)
1177 if (num_presets > 0)
1179 struct fmstation * const fms = &presets[selected_preset];
1181 if (selected_preset >= --num_presets)
1182 selected_preset = num_presets - 1;
1184 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1185 (uintptr_t)fms);
1189 /* Don't ask to save when all presets are deleted. */
1190 presets_changed = num_presets > 0;
1192 if (!presets_changed)
1194 /* The preset list will be cleared, switch to Scan Mode. */
1195 radio_mode = RADIO_SCAN_MODE;
1196 presets_loaded = false;
1199 return 1;
1202 static int load_preset_list(void)
1204 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1207 static int save_preset_list(void)
1209 if(num_presets > 0)
1211 bool bad_file_name = true;
1213 if(!dir_exists(FMPRESET_PATH)) /* Check if there is preset folder */
1214 mkdir(FMPRESET_PATH);
1216 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1217 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1219 while(bad_file_name)
1221 if(!kbd_input(filepreset, sizeof(filepreset)))
1223 /* check the name: max MAX_FILENAME (20) chars */
1224 char* p2;
1225 char* p1;
1226 int len;
1227 p1 = strrchr(filepreset, '/');
1228 p2 = p1;
1229 while((p1) && (*p2) && (*p2 != '.'))
1230 p2++;
1231 len = (int)(p2-p1) - 1;
1232 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1234 /* no slash, too long or too short */
1235 gui_syncsplash(HZ, ID2P(LANG_INVALID_FILENAME));
1237 else
1239 /* add correct extension (easier to always write)
1240 at this point, p2 points to 0 or the extension dot */
1241 *p2 = '\0';
1242 strcat(filepreset,".fmr");
1243 bad_file_name = false;
1244 radio_save_presets();
1247 else
1249 /* user aborted */
1250 return false;
1254 else
1255 gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS));
1257 return true;
1260 static int clear_preset_list(void)
1262 /* Clear all the preset entries */
1263 memset(presets, 0, sizeof (presets));
1265 num_presets = 0;
1266 presets_loaded = false;
1267 /* The preset list will be cleared switch to Scan Mode. */
1268 radio_mode = RADIO_SCAN_MODE;
1270 presets_changed = false; /* Don't ask to save when clearing the list. */
1272 return true;
1275 MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1276 ID2P(LANG_FM_EDIT_PRESET),
1277 radio_edit_preset, NULL, NULL, Icon_NOICON);
1278 MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1279 ID2P(LANG_FM_DELETE_PRESET),
1280 radio_delete_preset, NULL, NULL, Icon_NOICON);
1281 static int radio_preset_callback(int action,
1282 const struct menu_item_ex *this_item)
1284 if (action == ACTION_STD_OK)
1285 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1286 return action;
1287 (void)this_item;
1289 MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_PRESET),
1290 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1291 &radio_delete_preset_item);
1292 /* present a list of preset stations */
1293 static char * presets_get_name(int selected_item, void *data,
1294 char *buffer, size_t buffer_len)
1296 (void)data;
1297 struct fmstation *p = &presets[selected_item];
1298 if(p->name[0])
1299 return p->name;
1300 int freq = p->frequency / 10000;
1301 int frac = freq % 100;
1302 freq /= 100;
1303 snprintf(buffer, buffer_len,
1304 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1305 return buffer;
1308 static int presets_speak_name(int selected_item, void * data)
1310 (void)data;
1311 talk_preset(selected_item, true, false);
1312 return 0;
1315 static int handle_radio_presets(void)
1317 struct gui_synclist lists;
1318 int result = 0;
1319 int action = ACTION_NONE;
1320 #ifdef HAS_BUTTONBAR
1321 struct gui_buttonbar buttonbar;
1322 #endif
1324 if(presets_loaded == false)
1325 return result;
1327 #ifdef HAS_BUTTONBAR
1328 gui_buttonbar_init(&buttonbar);
1329 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1330 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1331 str(LANG_FM_BUTTONBAR_EXIT),
1332 str(LANG_FM_BUTTONBAR_ACTION));
1333 gui_buttonbar_draw(&buttonbar);
1334 #endif
1335 gui_synclist_init(&lists, presets_get_name, NULL, false, 1, NULL);
1336 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
1337 gui_synclist_set_icon_callback(&lists, NULL);
1338 if(global_settings.talk_file)
1339 gui_synclist_set_voice_callback(&lists, presets_speak_name);
1340 gui_synclist_set_nb_items(&lists, num_presets);
1341 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1342 gui_synclist_speak_item(&lists);
1344 while (result == 0)
1346 gui_synclist_draw(&lists);
1347 gui_syncstatusbar_draw(&statusbars, true);
1348 list_do_action(CONTEXT_STD, HZ,
1349 &lists, &action, LIST_WRAP_UNLESS_HELD);
1350 switch (action)
1352 case ACTION_STD_MENU:
1353 radio_add_preset();
1354 break;
1355 case ACTION_STD_CANCEL:
1356 result = 1;
1357 break;
1358 case ACTION_STD_OK:
1359 curr_preset = gui_synclist_get_sel_pos(&lists);
1360 curr_freq = presets[curr_preset].frequency;
1361 next_station(0);
1362 remember_frequency();
1363 result = 1;
1364 break;
1365 case ACTION_F3:
1366 case ACTION_STD_CONTEXT:
1367 selected_preset = gui_synclist_get_sel_pos(&lists);
1368 do_menu(&handle_radio_preset_menu, NULL, NULL, false);
1369 gui_synclist_speak_item(&lists);
1370 break;
1371 default:
1372 if(default_event_handler(action) == SYS_USB_CONNECTED)
1373 result = 2;
1376 return result - 1;
1379 void toggle_mono_mode(bool mono)
1381 tuner_set(RADIO_FORCE_MONO, mono);
1384 void set_radio_region(int region)
1386 #ifdef HAVE_RADIO_REGION
1387 tuner_set(RADIO_REGION, region);
1388 #endif
1389 next_station(0);
1390 remember_frequency();
1391 (void)region;
1394 MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
1395 MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
1397 #ifndef FM_MODE
1398 static char* get_mode_text(int selected_item, void * data, char *buffer)
1400 (void)selected_item;
1401 (void)data;
1402 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_MODE),
1403 radio_mode ? str(LANG_PRESET) :
1404 str(LANG_RADIO_SCAN_MODE));
1405 return buffer;
1407 static int toggle_radio_mode(void)
1409 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
1410 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
1411 return 0;
1413 MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1414 toggle_radio_mode, NULL,
1415 get_mode_text, NULL, NULL, NULL, Icon_NOICON);
1416 #endif
1418 static int scan_presets(void)
1420 bool do_scan = true;
1422 if(num_presets > 0) /* Do that to avoid 2 questions. */
1423 do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS));
1425 if(do_scan)
1427 const struct fm_region_data * const fmr =
1428 &fm_region_data[global_settings.fm_region];
1430 char buf[MAX_FMPRESET_LEN + 1];
1431 int i;
1433 curr_freq = fmr->freq_min;
1434 num_presets = 0;
1435 memset(presets, 0, sizeof(presets));
1436 tuner_set(RADIO_MUTE, 1);
1438 while(curr_freq <= fmr->freq_max)
1440 int freq, frac;
1441 if (num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1442 break;
1444 freq = curr_freq / 10000;
1445 frac = freq % 100;
1446 freq /= 100;
1448 snprintf(buf, MAX_FMPRESET_LEN, str(LANG_FM_SCANNING), freq, frac);
1449 gui_syncsplash(0, buf);
1451 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
1453 /* add preset */
1454 presets[num_presets].name[0] = '\0';
1455 presets[num_presets].frequency = curr_freq;
1456 num_presets++;
1459 curr_freq += fmr->freq_step;
1462 if (radio_status == FMRADIO_PLAYING)
1463 tuner_set(RADIO_MUTE, 0);
1465 presets_changed = true;
1467 FOR_NB_SCREENS(i)
1469 gui_textarea_clear(&screens[i]);
1470 screen_set_xmargin(&screens[i],0);
1471 gui_textarea_update(&screens[i]);
1474 if(num_presets > 0)
1476 curr_freq = presets[0].frequency;
1477 radio_mode = RADIO_PRESET_MODE;
1478 presets_loaded = true;
1479 next_station(0);
1481 else
1483 /* Wrap it to beginning or we'll be past end of band */
1484 presets_loaded = false;
1485 next_station(1);
1488 return true;
1492 #ifdef HAVE_RECORDING
1494 #if defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC
1495 #define FM_RECORDING_SCREEN
1496 static int fm_recording_screen(void)
1498 bool ret;
1500 /* switch recording source to FMRADIO for the duration */
1501 int rec_source = global_settings.rec_source;
1502 global_settings.rec_source = AUDIO_SRC_FMRADIO;
1504 ret = recording_screen(true);
1506 /* safe to reset as changing sources is prohibited here */
1507 global_settings.rec_source = rec_source;
1509 return ret;
1512 #endif /* defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC */
1514 #if defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC
1515 #define FM_RECORDING_SETTINGS
1516 static int fm_recording_settings(void)
1518 bool ret = recording_menu(true);
1520 #if CONFIG_CODEC != SWCODEC
1521 if (!ret)
1523 struct audio_recording_options rec_options;
1524 rec_init_recording_options(&rec_options);
1525 rec_options.rec_source = AUDIO_SRC_LINEIN;
1526 rec_set_recording_options(&rec_options);
1528 #endif
1530 return ret;
1533 #endif /* defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC */
1534 #endif /* HAVE_RECORDING */
1536 #ifdef FM_RECORDING_SCREEN
1537 MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING),
1538 fm_recording_screen, NULL, NULL, Icon_Recording);
1539 #endif
1540 #ifdef FM_RECORDING_SETTINGS
1541 MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1542 fm_recording_settings, NULL, NULL, Icon_Recording);
1543 #endif
1544 #ifndef FM_PRESET
1545 MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
1546 handle_radio_presets, NULL, NULL, Icon_NOICON);
1547 #endif
1548 #ifndef FM_PRESET_ADD
1549 MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1550 radio_add_preset, NULL, NULL, Icon_NOICON);
1551 #endif
1554 MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1555 load_preset_list, NULL, NULL, Icon_NOICON);
1556 MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1557 save_preset_list, NULL, NULL, Icon_NOICON);
1558 MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1559 clear_preset_list, NULL, NULL, Icon_NOICON);
1560 MENUITEM_FUNCTION(scan_presets_item, 0, ID2P(LANG_FM_SCAN_PRESETS),
1561 scan_presets, NULL, NULL, Icon_NOICON);
1563 MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
1564 Icon_Radio_screen,
1565 #ifndef FM_PRESET
1566 &radio_presets_item,
1567 #endif
1568 #ifndef FM_PRESET_ADD
1569 &radio_addpreset_item,
1570 #endif
1571 &presetload_item, &presetsave_item, &presetclear_item,
1572 &force_mono,
1573 #ifndef FM_MODE
1574 &radio_mode_item,
1575 #endif
1576 &set_region, &sound_settings,
1577 #ifdef FM_RECORDING_SCREEN
1578 &recscreen_item,
1579 #endif
1580 #ifdef FM_RECORDING_SETTINGS
1581 &recsettings_item,
1582 #endif
1583 &scan_presets_item);
1584 /* main menu of the radio screen */
1585 static bool radio_menu(void)
1587 return do_menu(&radio_settings_menu, NULL, NULL, false) == MENU_ATTACHED_USB;
1590 #endif