Accept FS #9052 by Alexander Levin with a fix by me. Changes HAS_BUTTONBAR into HAVE_...
[maemo-rb.git] / apps / recorder / radio.c
blobed6744ace8e3661128018bed63382a4be0e94698
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 "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"
66 #include "viewport.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 *viewports);
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 screens[i].clear_viewport();
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 struct viewport vp[NB_SCREENS];
455 #ifdef HAVE_BUTTONBAR
456 struct gui_buttonbar buttonbar;
457 gui_buttonbar_init(&buttonbar);
458 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
459 #endif
461 /* change status to "in screen" */
462 in_screen = true;
464 /* always display status bar in radio screen for now */
465 global_status.statusbar_forced = statusbar?0:1;
466 global_settings.statusbar = true;
467 gui_syncstatusbar_draw(&statusbars,true);
468 FOR_NB_SCREENS(i)
470 viewport_set_defaults(&vp[i], i);
471 #ifdef HAVE_BUTTONBAR
472 if (global_settings.buttonbar)
473 vp[i].height -= BUTTONBAR_HEIGHT;
474 #endif
475 screens[i].set_viewport(&vp[i]);
476 screens[i].clear_viewport();
477 screens[i].update_viewport();
480 fh = font_get(FONT_UI)->height;
482 /* Adjust for font size, trying to center the information vertically */
483 if(fh < 10)
484 top_of_screen = 1;
486 if(num_presets <= 0)
488 memset(presets, 0, sizeof(presets));
489 radio_load_presets(global_settings.fmr_file);
492 if(radio_status == FMRADIO_OFF)
493 audio_stop();
494 #ifndef SIMULATOR
496 #if CONFIG_CODEC != SWCODEC
497 if(rec_create_directory() > 0)
498 have_recorded = true;
500 audio_init_recording(talk_get_bufsize());
502 sound_settings_apply();
503 /* Yes, we use the D/A for monitoring */
504 peak_meter_playback(true);
506 peak_meter_enabled = true;
508 rec_init_recording_options(&rec_options);
509 rec_options.rec_source = AUDIO_SRC_LINEIN;
510 rec_set_recording_options(&rec_options);
512 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
513 sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
515 #endif /* CONFIG_CODEC != SWCODEC */
516 #endif /* ndef SIMULATOR */
518 /* turn on radio */
519 #if CONFIG_CODEC == SWCODEC
520 audio_set_input_source(AUDIO_SRC_FMRADIO,
521 (radio_status == FMRADIO_PAUSED) ?
522 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
523 #else
524 if (radio_status == FMRADIO_OFF)
525 radio_start();
526 #endif
528 if(num_presets < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN)))
529 scan_presets(vp);
531 curr_preset = find_preset(curr_freq);
532 if(curr_preset != -1)
533 radio_mode = RADIO_PRESET_MODE;
535 #ifdef HAVE_BUTTONBAR
536 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
537 str(LANG_PRESET), str(LANG_FM_BUTTONBAR_RECORD));
538 #endif
540 #ifndef HAVE_NOISY_IDLE_MODE
541 cpu_idle_mode(true);
542 #endif
544 while(!done)
546 if(search_dir != 0)
548 curr_freq = step_freq(curr_freq, search_dir);
549 update_screen = true;
551 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
553 curr_preset = find_preset(curr_freq);
554 remember_frequency();
555 end_search();
556 talk = true;
559 trigger_cpu_boost();
562 if (!update_screen)
564 cancel_cpu_boost();
567 #if CONFIG_CODEC != SWCODEC
568 /* TODO: Can we timeout at HZ when recording since peaks aren't
569 displayed? This should quiet recordings too. */
570 button = get_action(CONTEXT_FM,
571 update_screen ? TIMEOUT_NOBLOCK : HZ / PEAK_METER_FPS);
572 #else
573 button = get_action(CONTEXT_FM,
574 update_screen ? TIMEOUT_NOBLOCK : HZ);
575 #endif
577 #ifndef HAVE_NOISY_IDLE_MODE
578 if (button != ACTION_NONE)
580 cpu_idle_mode(false);
581 button_timeout = current_tick + (2*HZ);
583 #endif
584 switch(button)
586 case ACTION_FM_STOP:
587 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
588 if(audio_status() == AUDIO_STATUS_RECORD)
590 audio_stop();
592 else
593 #endif
595 done = true;
596 if(presets_changed)
598 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
600 if(filepreset[0] == '\0')
601 save_preset_list();
602 else
603 radio_save_presets();
606 /* Clear the preset list on exit. */
607 clear_preset_list();
609 update_screen = true;
610 break;
612 #ifdef FM_RECORD
613 case ACTION_FM_RECORD:
614 #ifdef FM_RECORD_DBLPRE
615 if (lastbutton != ACTION_FM_RECORD_DBLPRE)
617 rec_lastclick = 0;
618 break;
620 if (current_tick - rec_lastclick > HZ/2)
622 rec_lastclick = current_tick;
623 break;
625 #endif /* FM_RECORD_DBLPRE */
626 #ifndef SIMULATOR
627 if(audio_status() == AUDIO_STATUS_RECORD)
629 rec_command(RECORDING_CMD_START_NEWFILE);
630 update_screen = true;
632 else
634 have_recorded = true;
635 rec_command(RECORDING_CMD_START);
636 update_screen = true;
638 #endif /* SIMULATOR */
639 last_seconds = 0;
640 break;
641 #endif /* #ifdef FM_RECORD */
643 case ACTION_FM_EXIT:
644 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
645 if(audio_status() == AUDIO_STATUS_RECORD)
646 audio_stop();
647 #endif
648 keep_playing = true;
649 done = true;
650 ret_val = GO_TO_ROOT;
651 if(presets_changed)
653 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
655 if(filepreset[0] == '\0')
656 save_preset_list();
657 else
658 radio_save_presets();
662 /* Clear the preset list on exit. */
663 clear_preset_list();
665 break;
667 case ACTION_STD_PREV:
668 case ACTION_STD_NEXT:
669 next_station(button == ACTION_STD_PREV ? -1 : 1);
670 end_search();
671 update_screen = true;
672 talk = true;
673 break;
675 case ACTION_STD_PREVREPEAT:
676 case ACTION_STD_NEXTREPEAT:
678 int dir = search_dir;
679 search_dir = button == ACTION_STD_PREVREPEAT ? -1 : 1;
680 if (radio_mode != RADIO_SCAN_MODE)
682 next_preset(search_dir);
683 end_search();
684 update_screen = true;
685 talk = true;
687 else if (dir == 0)
689 /* Starting auto scan */
690 tuner_set(RADIO_MUTE, 1);
691 update_screen = true;
693 break;
696 case ACTION_SETTINGS_INC:
697 case ACTION_SETTINGS_INCREPEAT:
698 global_settings.volume++;
699 setvol();
700 update_screen = true;
701 break;
703 case ACTION_SETTINGS_DEC:
704 case ACTION_SETTINGS_DECREPEAT:
705 global_settings.volume--;
706 setvol();
707 update_screen = true;
708 break;
710 case ACTION_FM_PLAY:
711 if (radio_status == FMRADIO_PLAYING)
712 radio_pause();
713 else
714 radio_start();
716 update_screen = true;
717 talk = false;
718 talk_shutup();
719 break;
721 case ACTION_FM_MENU:
722 radio_menu();
723 curr_preset = find_preset(curr_freq);
724 FOR_NB_SCREENS(i)
726 screens[i].set_viewport(&vp[i]);
727 screens[i].clear_viewport();
728 screens[i].update_viewport();
729 screens[i].set_viewport(NULL);
731 #ifdef HAVE_BUTTONBAR
732 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
733 str(LANG_PRESET),
734 str(LANG_FM_BUTTONBAR_RECORD));
735 #endif
736 update_screen = true;
737 break;
739 #ifdef FM_PRESET
740 case ACTION_FM_PRESET:
741 if(num_presets < 1)
743 gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS));
744 update_screen = true;
745 FOR_NB_SCREENS(i)
747 screens[i].set_viewport(&vp[i]);
748 screens[i].clear_viewport();
749 screens[i].update_viewport();
750 screens[i].set_viewport(NULL);
753 break;
755 handle_radio_presets();
756 FOR_NB_SCREENS(i)
758 screens[i].set_viewport(&vp[i]);
759 screens[i].clear_viewport();
760 screens[i].update_viewport();
761 screens[i].set_viewport(NULL);
763 #ifdef HAVE_BUTTONBAR
764 gui_buttonbar_set(&buttonbar,
765 str(LANG_BUTTONBAR_MENU),
766 str(LANG_PRESET),
767 str(LANG_FM_BUTTONBAR_RECORD));
768 #endif
769 update_screen = true;
770 break;
771 #endif /* FM_PRESET */
773 #ifdef FM_FREEZE
774 case ACTION_FM_FREEZE:
775 if(!screen_freeze)
777 gui_syncsplash(HZ, str(LANG_FM_FREEZE));
778 screen_freeze = true;
780 else
782 update_screen = true;
783 screen_freeze = false;
785 break;
786 #endif /* FM_FREEZE */
788 case SYS_USB_CONNECTED:
789 #if CONFIG_CODEC != SWCODEC
790 /* Only accept USB connection when not recording */
791 if(audio_status() != AUDIO_STATUS_RECORD)
792 #endif
794 default_event_handler(SYS_USB_CONNECTED);
795 screen_freeze = true; /* Cosmetic: makes sure the
796 radio screen doesn't redraw */
797 done = true;
799 break;
801 #ifdef FM_MODE
802 case ACTION_FM_MODE:
803 if(radio_mode == RADIO_SCAN_MODE)
805 /* Force scan mode if there are no presets. */
806 if(num_presets > 0)
807 radio_mode = RADIO_PRESET_MODE;
809 else
810 radio_mode = RADIO_SCAN_MODE;
811 update_screen = true;
812 cond_talk_ids_fq(radio_mode ?
813 LANG_PRESET : LANG_RADIO_SCAN_MODE);
814 talk = true;
815 break;
816 #endif /* FM_MODE */
818 #ifdef FM_NEXT_PRESET
819 case ACTION_FM_NEXT_PRESET:
820 next_preset(1);
821 end_search();
822 update_screen = true;
823 talk = true;
824 break;
825 #endif
827 #ifdef FM_PREV_PRESET
828 case ACTION_FM_PREV_PRESET:
829 next_preset(-1);
830 end_search();
831 update_screen = true;
832 talk = true;
833 break;
834 #endif
836 default:
837 default_event_handler(button);
838 break;
839 } /*switch(button)*/
841 #ifdef FM_RECORD_DBLPRE
842 if (button != ACTION_NONE)
843 lastbutton = button;
844 #endif
846 #if CONFIG_CODEC != SWCODEC
847 peak_meter_peek();
848 #endif
850 if(!screen_freeze)
852 /* Only display the peak meter when not recording */
853 #if CONFIG_CODEC != SWCODEC
854 if(!audio_status())
856 FOR_NB_SCREENS(i)
858 screens[i].set_viewport(&vp[i]);
859 peak_meter_screen(&screens[i],0,
860 STATUSBAR_HEIGHT + fh*(top_of_screen + 4), fh);
861 screens[i].update_rect(0, STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
862 screens[i].width, fh);
863 screens[i].set_viewport(NULL);
867 if(TIME_AFTER(current_tick, timeout))
869 timeout = current_tick + HZ;
870 #else /* SWCODEC */
872 #endif /* CONFIG_CODEC == SWCODEC */
874 /* keep "mono" from always being displayed when paused */
875 if (radio_status != FMRADIO_PAUSED)
877 stereo = tuner_get(RADIO_STEREO) &&
878 !global_settings.fm_force_mono;
880 if(stereo != last_stereo)
882 update_screen = true;
883 last_stereo = stereo;
888 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
889 seconds = audio_recorded_time() / HZ;
890 if (update_screen || seconds > last_seconds)
892 last_seconds = seconds;
893 #else
894 if (update_screen)
896 #endif
897 int freq;
899 FOR_NB_SCREENS(i)
901 screens[i].set_viewport(&vp[i]);
904 snprintf(buf, 128, curr_preset >= 0 ? "%d. %s" : " ",
905 curr_preset + 1, presets[curr_preset].name);
907 FOR_NB_SCREENS(i)
908 screens[i].puts_scroll(0, top_of_screen, buf);
910 freq = curr_freq / 10000;
911 snprintf(buf, 128, str(LANG_FM_STATION), freq / 100, freq % 100);
912 FOR_NB_SCREENS(i)
913 screens[i].puts_scroll(0, top_of_screen + 1, buf);
915 snprintf(buf, 128, stereo?str(LANG_CHANNEL_STEREO):
916 str(LANG_CHANNEL_MONO));
917 FOR_NB_SCREENS(i)
918 screens[i].puts_scroll(0, top_of_screen + 2, buf);
920 snprintf(buf, 128, "%s %s", str(LANG_MODE),
921 radio_mode ? str(LANG_PRESET) :
922 str(LANG_RADIO_SCAN_MODE));
923 FOR_NB_SCREENS(i)
924 screens[i].puts_scroll(0, top_of_screen + 3, buf);
926 #if CONFIG_CODEC != SWCODEC
927 if(audio_status() == AUDIO_STATUS_RECORD)
929 hours = seconds / 3600;
930 minutes = (seconds - (hours * 3600)) / 60;
931 snprintf(buf, 32, "%s %02d:%02d:%02d",
932 str(LANG_RECORDING_TIME),
933 hours, minutes, seconds%60);
934 FOR_NB_SCREENS(i)
935 screens[i].puts_scroll(0, top_of_screen + 4, buf);
937 else
939 if(rec_options.rec_prerecord_time)
941 snprintf(buf, 32, "%s %02d",
942 str(LANG_RECORD_PRERECORD), seconds%60);
943 FOR_NB_SCREENS(i)
944 screens[i].puts_scroll(0, top_of_screen + 4, buf);
947 #endif /* CONFIG_CODEC != SWCODEC */
949 FOR_NB_SCREENS(i)
951 screens[i].update_viewport();
952 screens[i].set_viewport(NULL);
955 #ifdef HAVE_BUTTONBAR
956 gui_buttonbar_draw(&buttonbar);
957 #endif
959 /* Only force the redraw if update_screen is true */
960 gui_syncstatusbar_draw(&statusbars,true);
963 update_screen = false;
965 if (global_settings.talk_file && talk
966 && radio_status == FMRADIO_PAUSED)
968 talk = false;
969 bool enqueue = false;
970 if (radio_mode == RADIO_SCAN_MODE)
972 talk_freq(curr_freq, enqueue);
973 enqueue = true;
975 if (curr_preset >= 0)
976 talk_preset(curr_preset, radio_mode == RADIO_PRESET_MODE,
977 enqueue);
980 #if CONFIG_CODEC != SWCODEC
981 if(audio_status() & AUDIO_STATUS_ERROR)
983 done = true;
985 #endif
987 #ifndef HAVE_NOISY_IDLE_MODE
988 if (TIME_AFTER(current_tick, button_timeout))
990 cpu_idle_mode(true);
992 #endif
993 } /*while(!done)*/
995 #ifndef SIMULATOR
996 #if CONFIG_CODEC != SWCODEC
997 if(audio_status() & AUDIO_STATUS_ERROR)
999 gui_syncsplash(0, str(LANG_DISK_FULL));
1000 gui_syncstatusbar_draw(&statusbars,true);
1001 FOR_NB_SCREENS(i)
1003 screens[i].set_viewport(&vp[i]);
1004 screens[i].update_viewport();
1005 screens[i].set_viewport(NULL);
1007 audio_error_clear();
1009 while(1)
1011 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
1012 if(button == ACTION_FM_STOP)
1013 break;
1017 audio_init_playback();
1018 #endif /* CONFIG_CODEC != SWCODEC */
1020 sound_settings_apply();
1021 #endif /* SIMULATOR */
1023 if(keep_playing)
1025 /* Catch FMRADIO_PLAYING status for the sim. */
1026 #ifndef SIMULATOR
1027 #if CONFIG_CODEC != SWCODEC
1028 /* Enable the Left and right A/D Converter */
1029 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
1030 sound_default(SOUND_RIGHT_GAIN),
1031 AUDIO_GAIN_LINEIN);
1032 mas_codec_writereg(6, 0x4000);
1033 #endif
1034 end_search();
1035 #endif /* SIMULATOR */
1037 else
1039 #if CONFIG_CODEC == SWCODEC
1040 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
1041 #else
1042 radio_stop();
1043 #endif
1046 #ifndef HAVE_NOISY_IDLE_MODE
1047 cpu_idle_mode(false);
1048 #endif
1050 /* restore status bar settings */
1051 global_settings.statusbar = statusbar;
1052 global_status.statusbar_forced = 0;
1053 in_screen = false;
1054 #if CONFIG_CODEC != SWCODEC
1055 return have_recorded;
1056 #else
1057 return false;
1058 #endif
1059 } /* radio_screen */
1061 static void radio_save_presets(void)
1063 int fd;
1064 int i;
1066 fd = creat(filepreset);
1067 if(fd >= 0)
1069 for(i = 0;i < num_presets;i++)
1071 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
1073 close(fd);
1075 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
1076 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
1077 presets_changed = false;
1079 else
1081 gui_syncsplash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED));
1085 void radio_load_presets(char *filename)
1087 int fd;
1088 int rc;
1089 char buf[128];
1090 char *freq;
1091 char *name;
1092 bool done = false;
1093 int f;
1095 memset(presets, 0, sizeof(presets));
1096 num_presets = 0;
1098 /* No Preset in configuration. */
1099 if(filename[0] == '\0')
1101 filepreset[0] = '\0';
1102 return;
1104 /* Temporary preset, loaded until player shuts down. */
1105 else if(filename[0] == '/')
1106 strncpy(filepreset, filename, sizeof(filepreset));
1107 /* Preset from default directory. */
1108 else
1109 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1110 FMPRESET_PATH, filename);
1112 fd = open(filepreset, O_RDONLY);
1113 if(fd >= 0)
1115 while(!done && num_presets < MAX_PRESETS)
1117 rc = read_line(fd, buf, 128);
1118 if(rc > 0)
1120 if(settings_parseline(buf, &freq, &name))
1122 f = atoi(freq);
1123 if(f) /* For backwards compatibility */
1125 struct fmstation * const fms = &presets[num_presets];
1126 fms->frequency = f;
1127 strncpy(fms->name, name, MAX_FMPRESET_LEN);
1128 fms->name[MAX_FMPRESET_LEN] = '\0';
1129 num_presets++;
1133 else
1134 done = true;
1136 close(fd);
1138 else /* invalid file name? */
1139 filepreset[0] = '\0';
1141 presets_loaded = num_presets > 0;
1142 presets_changed = false;
1146 static int radio_add_preset(void)
1148 char buf[MAX_FMPRESET_LEN + 1];
1150 if(num_presets < MAX_PRESETS)
1152 memset(buf, 0, MAX_FMPRESET_LEN);
1154 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1156 struct fmstation * const fms = &presets[num_presets];
1157 buf[MAX_FMPRESET_LEN] = '\0';
1158 strcpy(fms->name, buf);
1159 fms->frequency = curr_freq;
1160 num_presets++;
1161 presets_changed = true;
1162 presets_loaded = num_presets > 0;
1165 else
1167 gui_syncsplash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS));
1169 return true;
1172 /* needed to know which preset we are edit/delete-ing */
1173 static int selected_preset = -1;
1174 static int radio_edit_preset(void)
1176 char buf[MAX_FMPRESET_LEN + 1];
1178 if (num_presets > 0)
1180 struct fmstation * const fms = &presets[selected_preset];
1182 strncpy(buf, fms->name, MAX_FMPRESET_LEN);
1184 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1186 buf[MAX_FMPRESET_LEN] = '\0';
1187 strcpy(fms->name, buf);
1188 presets_changed = true;
1192 return 1;
1195 static int radio_delete_preset(void)
1197 if (num_presets > 0)
1199 struct fmstation * const fms = &presets[selected_preset];
1201 if (selected_preset >= --num_presets)
1202 selected_preset = num_presets - 1;
1204 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1205 (uintptr_t)fms);
1209 /* Don't ask to save when all presets are deleted. */
1210 presets_changed = num_presets > 0;
1212 if (!presets_changed)
1214 /* The preset list will be cleared, switch to Scan Mode. */
1215 radio_mode = RADIO_SCAN_MODE;
1216 presets_loaded = false;
1219 return 1;
1222 static int load_preset_list(void)
1224 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1227 static int save_preset_list(void)
1229 if(num_presets > 0)
1231 bool bad_file_name = true;
1233 if(!dir_exists(FMPRESET_PATH)) /* Check if there is preset folder */
1234 mkdir(FMPRESET_PATH);
1236 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1237 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1239 while(bad_file_name)
1241 if(!kbd_input(filepreset, sizeof(filepreset)))
1243 /* check the name: max MAX_FILENAME (20) chars */
1244 char* p2;
1245 char* p1;
1246 int len;
1247 p1 = strrchr(filepreset, '/');
1248 p2 = p1;
1249 while((p1) && (*p2) && (*p2 != '.'))
1250 p2++;
1251 len = (int)(p2-p1) - 1;
1252 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1254 /* no slash, too long or too short */
1255 gui_syncsplash(HZ, ID2P(LANG_INVALID_FILENAME));
1257 else
1259 /* add correct extension (easier to always write)
1260 at this point, p2 points to 0 or the extension dot */
1261 *p2 = '\0';
1262 strcat(filepreset,".fmr");
1263 bad_file_name = false;
1264 radio_save_presets();
1267 else
1269 /* user aborted */
1270 return false;
1274 else
1275 gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS));
1277 return true;
1280 static int clear_preset_list(void)
1282 /* Clear all the preset entries */
1283 memset(presets, 0, sizeof (presets));
1285 num_presets = 0;
1286 presets_loaded = false;
1287 /* The preset list will be cleared switch to Scan Mode. */
1288 radio_mode = RADIO_SCAN_MODE;
1290 presets_changed = false; /* Don't ask to save when clearing the list. */
1292 return true;
1295 MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1296 ID2P(LANG_FM_EDIT_PRESET),
1297 radio_edit_preset, NULL, NULL, Icon_NOICON);
1298 MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1299 ID2P(LANG_FM_DELETE_PRESET),
1300 radio_delete_preset, NULL, NULL, Icon_NOICON);
1301 static int radio_preset_callback(int action,
1302 const struct menu_item_ex *this_item)
1304 if (action == ACTION_STD_OK)
1305 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1306 return action;
1307 (void)this_item;
1309 MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_PRESET),
1310 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1311 &radio_delete_preset_item);
1312 /* present a list of preset stations */
1313 static char * presets_get_name(int selected_item, void *data,
1314 char *buffer, size_t buffer_len)
1316 (void)data;
1317 struct fmstation *p = &presets[selected_item];
1318 if(p->name[0])
1319 return p->name;
1320 int freq = p->frequency / 10000;
1321 int frac = freq % 100;
1322 freq /= 100;
1323 snprintf(buffer, buffer_len,
1324 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1325 return buffer;
1328 static int presets_speak_name(int selected_item, void * data)
1330 (void)data;
1331 talk_preset(selected_item, true, false);
1332 return 0;
1335 static int handle_radio_presets(void)
1337 struct gui_synclist lists;
1338 int result = 0;
1339 int action = ACTION_NONE;
1340 #ifdef HAVE_BUTTONBAR
1341 struct gui_buttonbar buttonbar;
1342 #endif
1344 if(presets_loaded == false)
1345 return result;
1347 #ifdef HAVE_BUTTONBAR
1348 gui_buttonbar_init(&buttonbar);
1349 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1350 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1351 str(LANG_FM_BUTTONBAR_EXIT),
1352 str(LANG_FM_BUTTONBAR_ACTION));
1353 gui_buttonbar_draw(&buttonbar);
1354 #endif
1355 gui_synclist_init(&lists, presets_get_name, NULL, false, 1, NULL);
1356 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
1357 gui_synclist_set_icon_callback(&lists, NULL);
1358 if(global_settings.talk_file)
1359 gui_synclist_set_voice_callback(&lists, presets_speak_name);
1360 gui_synclist_set_nb_items(&lists, num_presets);
1361 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1362 gui_synclist_speak_item(&lists);
1364 while (result == 0)
1366 gui_synclist_draw(&lists);
1367 gui_syncstatusbar_draw(&statusbars, true);
1368 list_do_action(CONTEXT_STD, HZ,
1369 &lists, &action, LIST_WRAP_UNLESS_HELD);
1370 switch (action)
1372 case ACTION_STD_MENU:
1373 radio_add_preset();
1374 break;
1375 case ACTION_STD_CANCEL:
1376 result = 1;
1377 break;
1378 case ACTION_STD_OK:
1379 curr_preset = gui_synclist_get_sel_pos(&lists);
1380 curr_freq = presets[curr_preset].frequency;
1381 next_station(0);
1382 remember_frequency();
1383 result = 1;
1384 break;
1385 case ACTION_F3:
1386 case ACTION_STD_CONTEXT:
1387 selected_preset = gui_synclist_get_sel_pos(&lists);
1388 do_menu(&handle_radio_preset_menu, NULL, NULL, false);
1389 gui_synclist_speak_item(&lists);
1390 break;
1391 default:
1392 if(default_event_handler(action) == SYS_USB_CONNECTED)
1393 result = 2;
1396 return result - 1;
1399 void toggle_mono_mode(bool mono)
1401 tuner_set(RADIO_FORCE_MONO, mono);
1404 void set_radio_region(int region)
1406 #ifdef HAVE_RADIO_REGION
1407 tuner_set(RADIO_REGION, region);
1408 #endif
1409 next_station(0);
1410 remember_frequency();
1411 (void)region;
1414 MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
1415 MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
1417 #ifndef FM_MODE
1418 static char* get_mode_text(int selected_item, void * data, char *buffer)
1420 (void)selected_item;
1421 (void)data;
1422 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_MODE),
1423 radio_mode ? str(LANG_PRESET) :
1424 str(LANG_RADIO_SCAN_MODE));
1425 return buffer;
1427 static int toggle_radio_mode(void)
1429 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
1430 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
1431 return 0;
1433 MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1434 toggle_radio_mode, NULL,
1435 get_mode_text, NULL, NULL, NULL, Icon_NOICON);
1436 #endif
1438 static int scan_presets(void *viewports)
1440 bool do_scan = true;
1441 int i;
1442 struct viewport *vp = (struct viewport *)viewports;
1444 FOR_NB_SCREENS(i)
1445 screens[i].set_viewport(vp?&vp[i]:NULL);
1446 if(num_presets > 0) /* Do that to avoid 2 questions. */
1447 do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS));
1449 if(do_scan)
1451 const struct fm_region_data * const fmr =
1452 &fm_region_data[global_settings.fm_region];
1454 char buf[MAX_FMPRESET_LEN + 1];
1456 curr_freq = fmr->freq_min;
1457 num_presets = 0;
1458 memset(presets, 0, sizeof(presets));
1459 tuner_set(RADIO_MUTE, 1);
1461 while(curr_freq <= fmr->freq_max)
1463 int freq, frac;
1464 if (num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1465 break;
1467 freq = curr_freq / 10000;
1468 frac = freq % 100;
1469 freq /= 100;
1471 snprintf(buf, MAX_FMPRESET_LEN, str(LANG_FM_SCANNING), freq, frac);
1472 gui_syncsplash(0, buf);
1474 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
1476 /* add preset */
1477 presets[num_presets].name[0] = '\0';
1478 presets[num_presets].frequency = curr_freq;
1479 num_presets++;
1482 curr_freq += fmr->freq_step;
1485 if (radio_status == FMRADIO_PLAYING)
1486 tuner_set(RADIO_MUTE, 0);
1488 presets_changed = true;
1490 FOR_NB_SCREENS(i)
1492 screens[i].clear_viewport();
1493 screens[i].update_viewport();
1496 if(num_presets > 0)
1498 curr_freq = presets[0].frequency;
1499 radio_mode = RADIO_PRESET_MODE;
1500 presets_loaded = true;
1501 next_station(0);
1503 else
1505 /* Wrap it to beginning or we'll be past end of band */
1506 presets_loaded = false;
1507 next_station(1);
1510 return true;
1514 #ifdef HAVE_RECORDING
1516 #if defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC
1517 #define FM_RECORDING_SCREEN
1518 static int fm_recording_screen(void)
1520 bool ret;
1522 /* switch recording source to FMRADIO for the duration */
1523 int rec_source = global_settings.rec_source;
1524 global_settings.rec_source = AUDIO_SRC_FMRADIO;
1526 ret = recording_screen(true);
1528 /* safe to reset as changing sources is prohibited here */
1529 global_settings.rec_source = rec_source;
1531 return ret;
1534 #endif /* defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC */
1536 #if defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC
1537 #define FM_RECORDING_SETTINGS
1538 static int fm_recording_settings(void)
1540 bool ret = recording_menu(true);
1542 #if CONFIG_CODEC != SWCODEC
1543 if (!ret)
1545 struct audio_recording_options rec_options;
1546 rec_init_recording_options(&rec_options);
1547 rec_options.rec_source = AUDIO_SRC_LINEIN;
1548 rec_set_recording_options(&rec_options);
1550 #endif
1552 return ret;
1555 #endif /* defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC */
1556 #endif /* HAVE_RECORDING */
1558 #ifdef FM_RECORDING_SCREEN
1559 MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING),
1560 fm_recording_screen, NULL, NULL, Icon_Recording);
1561 #endif
1562 #ifdef FM_RECORDING_SETTINGS
1563 MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1564 fm_recording_settings, NULL, NULL, Icon_Recording);
1565 #endif
1566 #ifndef FM_PRESET
1567 MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
1568 handle_radio_presets, NULL, NULL, Icon_NOICON);
1569 #endif
1570 #ifndef FM_PRESET_ADD
1571 MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1572 radio_add_preset, NULL, NULL, Icon_NOICON);
1573 #endif
1576 MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1577 load_preset_list, NULL, NULL, Icon_NOICON);
1578 MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1579 save_preset_list, NULL, NULL, Icon_NOICON);
1580 MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1581 clear_preset_list, NULL, NULL, Icon_NOICON);
1582 MENUITEM_FUNCTION(scan_presets_item, MENU_FUNC_USEPARAM,
1583 ID2P(LANG_FM_SCAN_PRESETS),
1584 scan_presets, NULL, NULL, Icon_NOICON);
1586 MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
1587 Icon_Radio_screen,
1588 #ifndef FM_PRESET
1589 &radio_presets_item,
1590 #endif
1591 #ifndef FM_PRESET_ADD
1592 &radio_addpreset_item,
1593 #endif
1594 &presetload_item, &presetsave_item, &presetclear_item,
1595 &force_mono,
1596 #ifndef FM_MODE
1597 &radio_mode_item,
1598 #endif
1599 &set_region, &sound_settings,
1600 #ifdef FM_RECORDING_SCREEN
1601 &recscreen_item,
1602 #endif
1603 #ifdef FM_RECORDING_SETTINGS
1604 &recsettings_item,
1605 #endif
1606 &scan_presets_item);
1607 /* main menu of the radio screen */
1608 static bool radio_menu(void)
1610 return do_menu(&radio_settings_menu, NULL, NULL, false) == MENU_ATTACHED_USB;
1613 #endif