Fix FS #9128: invalidate the peakmeter scales when switching between playback and...
[Rockbox.git] / apps / recorder / radio.c
blobd92bccb5b068ba3dbb9fb5a473d472ce28fc5aca
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2003 Linus Nielsen Feltzing
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "config.h"
23 #include <stdio.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include "sprintf.h"
27 #include "mas.h"
28 #include "settings.h"
29 #include "button.h"
30 #include "status.h"
31 #include "thread.h"
32 #include "mpeg.h"
33 #include "audio.h"
34 #include "mp3_playback.h"
35 #include "ctype.h"
36 #include "file.h"
37 #include "errno.h"
38 #include "string.h"
39 #include "system.h"
40 #include "radio.h"
41 #include "menu.h"
42 #include "misc.h"
43 #include "keyboard.h"
44 #include "screens.h"
45 #include "peakmeter.h"
46 #include "lang.h"
47 #include "font.h"
48 #include "sound_menu.h"
49 #ifdef HAVE_RECORDING
50 #include "recording.h"
51 #endif
52 #include "talk.h"
53 #include "tuner.h"
54 #include "power.h"
55 #include "sound.h"
56 #include "screen_access.h"
57 #include "statusbar.h"
58 #include "splash.h"
59 #include "yesno.h"
60 #include "buttonbar.h"
61 #include "power.h"
62 #include "tree.h"
63 #include "dir.h"
64 #include "action.h"
65 #include "list.h"
66 #include "menus/exported_menus.h"
67 #include "root_menu.h"
68 #include "viewport.h"
70 #if CONFIG_TUNER
72 #if CONFIG_KEYPAD == RECORDER_PAD
73 #define FM_RECORD
74 #define FM_PRESET_ADD
75 #define FM_PRESET_ACTION
76 #define FM_PRESET
77 #define FM_MODE
79 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
80 #define FM_PRESET
81 #define FM_MODE
82 #define FM_NEXT_PRESET
83 #define FM_PREV_PRESET
85 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
86 #define FM_PRESET
87 #define FM_MODE
89 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
90 #define FM_PRESET
91 #define FM_MODE
92 /* This should be removeable if the whole tuning thing is sorted out since
93 proper tuning quiets the screen almost entirely in that extreme measures
94 have to be taken to hear any interference. */
95 #define HAVE_NOISY_IDLE_MODE
97 #elif CONFIG_KEYPAD == ONDIO_PAD
98 #define FM_RECORD_DBLPRE
99 #define FM_RECORD
100 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || (CONFIG_KEYPAD == SANSA_C200_PAD)
101 #define FM_MENU
102 #define FM_PRESET
103 #define FM_STOP
104 #define FM_MODE
105 #define FM_EXIT
106 #define FM_PLAY
107 #endif
109 #define RADIO_SCAN_MODE 0
110 #define RADIO_PRESET_MODE 1
112 static int curr_preset = -1;
113 static int curr_freq;
114 static int radio_mode = RADIO_SCAN_MODE;
115 static int search_dir = 0;
117 static int radio_status = FMRADIO_OFF;
118 static bool in_screen = false;
120 #define MAX_PRESETS 64
121 static bool presets_loaded = false, presets_changed = false;
122 static struct fmstation presets[MAX_PRESETS];
124 static char filepreset[MAX_PATH]; /* preset filename variable */
126 static int num_presets = 0; /* The number of presets in the preset list */
128 static void radio_save_presets(void);
129 static int handle_radio_presets(void);
130 static bool radio_menu(void);
131 static int radio_add_preset(void);
132 static int save_preset_list(void);
133 static int load_preset_list(void);
134 static int clear_preset_list(void);
136 static int scan_presets(void *viewports);
138 /* Function to manipulate all yesno dialogues.
139 This function needs the output text as an argument. */
140 static bool yesno_pop(const char* text)
142 int i;
143 const char *lines[]={text};
144 const struct text_message message={lines, 1};
145 bool ret = (gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
146 FOR_NB_SCREENS(i)
147 screens[i].clear_viewport();
148 return ret;
151 void radio_init(void)
153 tuner_init();
154 radio_stop();
157 int get_radio_status(void)
159 return radio_status;
162 bool in_radio_screen(void)
164 return in_screen;
167 /* TODO: Move some more of the control functionality to an HAL and clean up the
168 mess */
170 /* secret flag for starting paused - prevents unmute */
171 #define FMRADIO_START_PAUSED 0x8000
172 void radio_start(void)
174 const struct fm_region_data *fmr;
175 bool start_paused;
177 if(radio_status == FMRADIO_PLAYING)
178 return;
180 fmr = &fm_region_data[global_settings.fm_region];
182 start_paused = radio_status & FMRADIO_START_PAUSED;
183 /* clear flag before any yielding */
184 radio_status &= ~FMRADIO_START_PAUSED;
186 if(radio_status == FMRADIO_OFF)
187 tuner_power(true);
189 curr_freq = global_status.last_frequency
190 * fmr->freq_step + fmr->freq_min;
192 tuner_set(RADIO_SLEEP, 0); /* wake up the tuner */
194 if(radio_status == FMRADIO_OFF)
196 #ifdef HAVE_RADIO_REGION
197 tuner_set(RADIO_REGION, global_settings.fm_region);
198 #endif
199 tuner_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
202 tuner_set(RADIO_FREQUENCY, curr_freq);
204 #ifdef HAVE_RADIO_MUTE_TIMEOUT
206 unsigned long mute_timeout = current_tick + HZ;
207 if (radio_status != FMRADIO_OFF)
209 /* paused */
210 mute_timeout += HZ;
213 while(!tuner_get(RADIO_STEREO) && !tuner_get(RADIO_TUNED))
215 if(TIME_AFTER(current_tick, mute_timeout))
216 break;
217 yield();
220 #endif
222 /* keep radio from sounding initially */
223 if(!start_paused)
224 tuner_set(RADIO_MUTE, 0);
226 radio_status = FMRADIO_PLAYING;
227 } /* radio_start */
229 void radio_pause(void)
231 if(radio_status == FMRADIO_PAUSED)
232 return;
234 if(radio_status == FMRADIO_OFF)
236 radio_status |= FMRADIO_START_PAUSED;
237 radio_start();
240 tuner_set(RADIO_MUTE, 1);
241 tuner_set(RADIO_SLEEP, 1);
243 radio_status = FMRADIO_PAUSED;
244 } /* radio_pause */
246 void radio_stop(void)
248 if(radio_status == FMRADIO_OFF)
249 return;
251 tuner_set(RADIO_MUTE, 1);
252 tuner_set(RADIO_SLEEP, 1); /* low power mode, if available */
253 radio_status = FMRADIO_OFF;
254 tuner_power(false); /* status update, power off if avail. */
255 } /* radio_stop */
257 bool radio_hardware_present(void)
259 return tuner_get(RADIO_PRESENT);
262 /* Keep freq on the grid for the current region */
263 static int snap_freq_to_grid(int freq)
265 const struct fm_region_data * const fmr =
266 &fm_region_data[global_settings.fm_region];
268 /* Range clamp if out of range or just round to nearest */
269 if (freq < fmr->freq_min)
270 freq = fmr->freq_min;
271 else if (freq > fmr->freq_max)
272 freq = fmr->freq_max;
273 else
274 freq = (freq - fmr->freq_min + fmr->freq_step/2) /
275 fmr->freq_step * fmr->freq_step + fmr->freq_min;
277 return freq;
280 /* Find a matching preset to freq */
281 static int find_preset(int freq)
283 int i;
284 if(num_presets < 1)
285 return -1;
286 for(i = 0;i < MAX_PRESETS;i++)
288 if(freq == presets[i].frequency)
289 return i;
292 return -1;
295 /* Return the first preset encountered in the search direction with
296 wraparound. */
297 static int find_closest_preset(int freq, int direction)
299 int i;
301 if (direction == 0) /* direction == 0 isn't really used */
302 return 0;
304 for (i = 0; i < MAX_PRESETS; i++)
306 int preset_frequency = presets[i].frequency;
308 if (preset_frequency == freq)
309 return i; /* Exact match = stop */
310 /* Stop when the preset frequency exeeds freq so that we can
311 pick the correct one based on direction */
312 if (preset_frequency > freq)
313 break;
316 /* wrap around depending on direction */
317 if (i == 0 || i >= num_presets - 1)
318 i = direction < 0 ? num_presets - 1 : 0;
319 else if (direction < 0)
320 i--; /* use previous */
322 return i;
325 static void remember_frequency(void)
327 const struct fm_region_data * const fmr =
328 &fm_region_data[global_settings.fm_region];
329 global_status.last_frequency = (curr_freq - fmr->freq_min)
330 / fmr->freq_step;
331 status_save();
334 static void next_preset(int direction)
336 if (num_presets < 1)
337 return;
339 if (curr_preset == -1)
340 curr_preset = find_closest_preset(curr_freq, direction);
341 else
342 curr_preset = (curr_preset + direction + num_presets) % num_presets;
344 /* Must stay on the current grid for the region */
345 curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
347 tuner_set(RADIO_FREQUENCY, curr_freq);
348 remember_frequency();
351 /* Step to the next or previous frequency */
352 static int step_freq(int freq, int direction)
354 const struct fm_region_data * const fmr =
355 &fm_region_data[global_settings.fm_region];
357 freq += direction*fmr->freq_step;
359 /* Wrap first or snapping to grid will not let us on the band extremes */
360 if (freq > fmr->freq_max)
361 freq = direction > 0 ? fmr->freq_min : fmr->freq_max;
362 else if (freq < fmr->freq_min)
363 freq = direction < 0 ? fmr->freq_max : fmr->freq_min;
364 else
365 freq = snap_freq_to_grid(freq);
367 return freq;
370 /* Step to the next or previous station */
371 static void next_station(int direction)
373 if (direction != 0 && radio_mode != RADIO_SCAN_MODE)
375 next_preset(direction);
376 return;
379 curr_freq = step_freq(curr_freq, direction);
381 if (radio_status == FMRADIO_PLAYING)
382 tuner_set(RADIO_MUTE, 1);
384 tuner_set(RADIO_FREQUENCY, curr_freq);
386 if (radio_status == FMRADIO_PLAYING)
387 tuner_set(RADIO_MUTE, 0);
389 curr_preset = find_preset(curr_freq);
390 remember_frequency();
393 /* Ends an in-progress search */
394 static void end_search(void)
396 if (search_dir != 0 && radio_status == FMRADIO_PLAYING)
397 tuner_set(RADIO_MUTE, 0);
398 search_dir = 0;
401 /* Speak a frequency. */
402 static void talk_freq(int freq, bool enqueue)
404 freq /= 10000;
405 talk_number(freq / 100, enqueue);
406 talk_id(LANG_POINT, true);
407 talk_number(freq % 100 / 10, true);
408 if (freq % 10)
409 talk_number(freq % 10, true);
412 /* Speak a preset by number or by spelling its name, depending on settings. */
413 static void talk_preset(int preset, bool fallback, bool enqueue)
415 if (global_settings.talk_file == 1) /* number */
416 talk_number(preset + 1, enqueue);
417 else
418 { /* spell */
419 if(presets[preset].name[0])
420 talk_spell(presets[preset].name, enqueue);
421 else if(fallback)
422 talk_freq(presets[preset].frequency, enqueue);
426 int radio_screen(void)
428 char buf[MAX_PATH];
429 bool done = false;
430 int ret_val = GO_TO_ROOT;
431 int button;
432 int i;
433 bool stereo = false, last_stereo = false;
434 int fh;
435 int top_of_screen = 0;
436 bool update_screen = true;
437 bool screen_freeze = false;
438 bool keep_playing = false;
439 bool statusbar = global_settings.statusbar;
440 bool talk = false;
441 #ifdef FM_RECORD_DBLPRE
442 int lastbutton = BUTTON_NONE;
443 unsigned long rec_lastclick = 0;
444 #endif
445 #if CONFIG_CODEC != SWCODEC
446 bool have_recorded = false;
447 int timeout = current_tick + HZ/10;
448 unsigned int seconds = 0;
449 unsigned int last_seconds = 0;
450 int hours, minutes;
451 struct audio_recording_options rec_options;
452 #endif /* CONFIG_CODEC != SWCODEC */
453 #ifndef HAVE_NOISY_IDLE_MODE
454 int button_timeout = current_tick + (2*HZ);
455 #endif
456 struct viewport vp[NB_SCREENS];
457 #ifdef HAVE_BUTTONBAR
458 struct gui_buttonbar buttonbar;
459 gui_buttonbar_init(&buttonbar);
460 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
461 #endif
463 /* change status to "in screen" */
464 in_screen = true;
466 /* always display status bar in radio screen for now */
467 global_status.statusbar_forced = statusbar?0:1;
468 global_settings.statusbar = true;
469 gui_syncstatusbar_draw(&statusbars,true);
470 FOR_NB_SCREENS(i)
472 viewport_set_defaults(&vp[i], i);
473 #ifdef HAVE_BUTTONBAR
474 if (global_settings.buttonbar)
475 vp[i].height -= BUTTONBAR_HEIGHT;
476 #endif
477 screens[i].set_viewport(&vp[i]);
478 screens[i].clear_viewport();
479 screens[i].update_viewport();
482 fh = font_get(FONT_UI)->height;
484 /* Adjust for font size, trying to center the information vertically */
485 if(fh < 10)
486 top_of_screen = 1;
488 if(num_presets <= 0)
490 memset(presets, 0, sizeof(presets));
491 radio_load_presets(global_settings.fmr_file);
494 if(radio_status == FMRADIO_OFF)
495 audio_stop();
496 #ifndef SIMULATOR
498 #if CONFIG_CODEC != SWCODEC
499 if(rec_create_directory() > 0)
500 have_recorded = true;
502 audio_init_recording(talk_get_bufsize());
504 sound_settings_apply();
505 /* Yes, we use the D/A for monitoring */
506 peak_meter_playback(true);
508 peak_meter_enabled = true;
510 rec_init_recording_options(&rec_options);
511 rec_options.rec_source = AUDIO_SRC_LINEIN;
512 rec_set_recording_options(&rec_options);
514 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
515 sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
517 #endif /* CONFIG_CODEC != SWCODEC */
518 #endif /* ndef SIMULATOR */
520 /* turn on radio */
521 #if CONFIG_CODEC == SWCODEC
522 audio_set_input_source(AUDIO_SRC_FMRADIO,
523 (radio_status == FMRADIO_PAUSED) ?
524 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
525 #else
526 if (radio_status == FMRADIO_OFF)
527 radio_start();
528 #endif
530 if(num_presets < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN)))
531 scan_presets(vp);
533 curr_preset = find_preset(curr_freq);
534 if(curr_preset != -1)
535 radio_mode = RADIO_PRESET_MODE;
537 #ifdef HAVE_BUTTONBAR
538 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
539 str(LANG_PRESET), str(LANG_FM_BUTTONBAR_RECORD));
540 #endif
542 #ifndef HAVE_NOISY_IDLE_MODE
543 cpu_idle_mode(true);
544 #endif
546 while(!done)
548 if(search_dir != 0)
550 curr_freq = step_freq(curr_freq, search_dir);
551 update_screen = true;
553 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
555 curr_preset = find_preset(curr_freq);
556 remember_frequency();
557 end_search();
558 talk = true;
561 trigger_cpu_boost();
564 if (!update_screen)
566 cancel_cpu_boost();
569 #if CONFIG_CODEC != SWCODEC
570 /* TODO: Can we timeout at HZ when recording since peaks aren't
571 displayed? This should quiet recordings too. */
572 button = get_action(CONTEXT_FM,
573 update_screen ? TIMEOUT_NOBLOCK : HZ / PEAK_METER_FPS);
574 #else
575 button = get_action(CONTEXT_FM,
576 update_screen ? TIMEOUT_NOBLOCK : HZ);
577 #endif
579 #ifndef HAVE_NOISY_IDLE_MODE
580 if (button != ACTION_NONE)
582 cpu_idle_mode(false);
583 button_timeout = current_tick + (2*HZ);
585 #endif
586 switch(button)
588 case ACTION_FM_STOP:
589 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
590 if(audio_status() == AUDIO_STATUS_RECORD)
592 audio_stop();
594 else
595 #endif
597 done = true;
598 if(presets_changed)
600 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
602 if(filepreset[0] == '\0')
603 save_preset_list();
604 else
605 radio_save_presets();
608 /* Clear the preset list on exit. */
609 clear_preset_list();
611 update_screen = true;
612 break;
614 #ifdef FM_RECORD
615 case ACTION_FM_RECORD:
616 #ifdef FM_RECORD_DBLPRE
617 if (lastbutton != ACTION_FM_RECORD_DBLPRE)
619 rec_lastclick = 0;
620 break;
622 if (current_tick - rec_lastclick > HZ/2)
624 rec_lastclick = current_tick;
625 break;
627 #endif /* FM_RECORD_DBLPRE */
628 #ifndef SIMULATOR
629 if(audio_status() == AUDIO_STATUS_RECORD)
631 rec_command(RECORDING_CMD_START_NEWFILE);
632 update_screen = true;
634 else
636 have_recorded = true;
637 rec_command(RECORDING_CMD_START);
638 update_screen = true;
640 #endif /* SIMULATOR */
641 last_seconds = 0;
642 break;
643 #endif /* #ifdef FM_RECORD */
645 case ACTION_FM_EXIT:
646 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
647 if(audio_status() == AUDIO_STATUS_RECORD)
648 audio_stop();
649 #endif
650 keep_playing = true;
651 done = true;
652 ret_val = GO_TO_ROOT;
653 if(presets_changed)
655 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
657 if(filepreset[0] == '\0')
658 save_preset_list();
659 else
660 radio_save_presets();
664 /* Clear the preset list on exit. */
665 clear_preset_list();
667 break;
669 case ACTION_STD_PREV:
670 case ACTION_STD_NEXT:
671 next_station(button == ACTION_STD_PREV ? -1 : 1);
672 end_search();
673 update_screen = true;
674 talk = true;
675 break;
677 case ACTION_STD_PREVREPEAT:
678 case ACTION_STD_NEXTREPEAT:
680 int dir = search_dir;
681 search_dir = button == ACTION_STD_PREVREPEAT ? -1 : 1;
682 if (radio_mode != RADIO_SCAN_MODE)
684 next_preset(search_dir);
685 end_search();
686 update_screen = true;
687 talk = true;
689 else if (dir == 0)
691 /* Starting auto scan */
692 tuner_set(RADIO_MUTE, 1);
693 update_screen = true;
695 break;
698 case ACTION_SETTINGS_INC:
699 case ACTION_SETTINGS_INCREPEAT:
700 global_settings.volume++;
701 setvol();
702 update_screen = true;
703 break;
705 case ACTION_SETTINGS_DEC:
706 case ACTION_SETTINGS_DECREPEAT:
707 global_settings.volume--;
708 setvol();
709 update_screen = true;
710 break;
712 case ACTION_FM_PLAY:
713 if (radio_status == FMRADIO_PLAYING)
714 radio_pause();
715 else
716 radio_start();
718 update_screen = true;
719 talk = false;
720 talk_shutup();
721 break;
723 case ACTION_FM_MENU:
724 radio_menu();
725 curr_preset = find_preset(curr_freq);
726 FOR_NB_SCREENS(i)
728 screens[i].set_viewport(&vp[i]);
729 screens[i].clear_viewport();
730 screens[i].update_viewport();
731 screens[i].set_viewport(NULL);
733 #ifdef HAVE_BUTTONBAR
734 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
735 str(LANG_PRESET),
736 str(LANG_FM_BUTTONBAR_RECORD));
737 #endif
738 update_screen = true;
739 break;
741 #ifdef FM_PRESET
742 case ACTION_FM_PRESET:
743 if(num_presets < 1)
745 gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS));
746 update_screen = true;
747 FOR_NB_SCREENS(i)
749 screens[i].set_viewport(&vp[i]);
750 screens[i].clear_viewport();
751 screens[i].update_viewport();
752 screens[i].set_viewport(NULL);
755 break;
757 handle_radio_presets();
758 FOR_NB_SCREENS(i)
760 screens[i].set_viewport(&vp[i]);
761 screens[i].clear_viewport();
762 screens[i].update_viewport();
763 screens[i].set_viewport(NULL);
765 #ifdef HAVE_BUTTONBAR
766 gui_buttonbar_set(&buttonbar,
767 str(LANG_BUTTONBAR_MENU),
768 str(LANG_PRESET),
769 str(LANG_FM_BUTTONBAR_RECORD));
770 #endif
771 update_screen = true;
772 break;
773 #endif /* FM_PRESET */
775 #ifdef FM_FREEZE
776 case ACTION_FM_FREEZE:
777 if(!screen_freeze)
779 gui_syncsplash(HZ, str(LANG_FM_FREEZE));
780 screen_freeze = true;
782 else
784 update_screen = true;
785 screen_freeze = false;
787 break;
788 #endif /* FM_FREEZE */
790 case SYS_USB_CONNECTED:
791 #if CONFIG_CODEC != SWCODEC
792 /* Only accept USB connection when not recording */
793 if(audio_status() != AUDIO_STATUS_RECORD)
794 #endif
796 default_event_handler(SYS_USB_CONNECTED);
797 screen_freeze = true; /* Cosmetic: makes sure the
798 radio screen doesn't redraw */
799 done = true;
801 break;
803 #ifdef FM_MODE
804 case ACTION_FM_MODE:
805 if(radio_mode == RADIO_SCAN_MODE)
807 /* Force scan mode if there are no presets. */
808 if(num_presets > 0)
809 radio_mode = RADIO_PRESET_MODE;
811 else
812 radio_mode = RADIO_SCAN_MODE;
813 update_screen = true;
814 cond_talk_ids_fq(radio_mode ?
815 LANG_PRESET : LANG_RADIO_SCAN_MODE);
816 talk = true;
817 break;
818 #endif /* FM_MODE */
820 #ifdef FM_NEXT_PRESET
821 case ACTION_FM_NEXT_PRESET:
822 next_preset(1);
823 end_search();
824 update_screen = true;
825 talk = true;
826 break;
827 #endif
829 #ifdef FM_PREV_PRESET
830 case ACTION_FM_PREV_PRESET:
831 next_preset(-1);
832 end_search();
833 update_screen = true;
834 talk = true;
835 break;
836 #endif
838 default:
839 default_event_handler(button);
840 break;
841 } /*switch(button)*/
843 #ifdef FM_RECORD_DBLPRE
844 if (button != ACTION_NONE)
845 lastbutton = button;
846 #endif
848 #if CONFIG_CODEC != SWCODEC
849 peak_meter_peek();
850 #endif
852 if(!screen_freeze)
854 /* Only display the peak meter when not recording */
855 #if CONFIG_CODEC != SWCODEC
856 if(!audio_status())
858 FOR_NB_SCREENS(i)
860 screens[i].set_viewport(&vp[i]);
861 peak_meter_screen(&screens[i],0,
862 STATUSBAR_HEIGHT + fh*(top_of_screen + 4), fh);
863 screens[i].update_rect(0, STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
864 screens[i].getwidth(), fh);
865 screens[i].set_viewport(NULL);
869 if(TIME_AFTER(current_tick, timeout))
871 timeout = current_tick + HZ;
872 #else /* SWCODEC */
874 #endif /* CONFIG_CODEC == SWCODEC */
876 /* keep "mono" from always being displayed when paused */
877 if (radio_status != FMRADIO_PAUSED)
879 stereo = tuner_get(RADIO_STEREO) &&
880 !global_settings.fm_force_mono;
882 if(stereo != last_stereo)
884 update_screen = true;
885 last_stereo = stereo;
890 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
891 seconds = audio_recorded_time() / HZ;
892 if (update_screen || seconds > last_seconds)
894 last_seconds = seconds;
895 #else
896 if (update_screen)
898 #endif
899 int freq;
901 FOR_NB_SCREENS(i)
903 screens[i].set_viewport(&vp[i]);
906 snprintf(buf, 128, curr_preset >= 0 ? "%d. %s" : " ",
907 curr_preset + 1, presets[curr_preset].name);
909 FOR_NB_SCREENS(i)
910 screens[i].puts_scroll(0, top_of_screen, buf);
912 freq = curr_freq / 10000;
913 snprintf(buf, 128, str(LANG_FM_STATION), freq / 100, freq % 100);
914 FOR_NB_SCREENS(i)
915 screens[i].puts_scroll(0, top_of_screen + 1, buf);
917 snprintf(buf, 128, stereo?str(LANG_CHANNEL_STEREO):
918 str(LANG_CHANNEL_MONO));
919 FOR_NB_SCREENS(i)
920 screens[i].puts_scroll(0, top_of_screen + 2, buf);
922 snprintf(buf, 128, "%s %s", str(LANG_MODE),
923 radio_mode ? str(LANG_PRESET) :
924 str(LANG_RADIO_SCAN_MODE));
925 FOR_NB_SCREENS(i)
926 screens[i].puts_scroll(0, top_of_screen + 3, buf);
928 #if CONFIG_CODEC != SWCODEC
929 if(audio_status() == AUDIO_STATUS_RECORD)
931 hours = seconds / 3600;
932 minutes = (seconds - (hours * 3600)) / 60;
933 snprintf(buf, 32, "%s %02d:%02d:%02d",
934 str(LANG_RECORDING_TIME),
935 hours, minutes, seconds%60);
936 FOR_NB_SCREENS(i)
937 screens[i].puts_scroll(0, top_of_screen + 4, buf);
939 else
941 if(rec_options.rec_prerecord_time)
943 snprintf(buf, 32, "%s %02d",
944 str(LANG_RECORD_PRERECORD), seconds%60);
945 FOR_NB_SCREENS(i)
946 screens[i].puts_scroll(0, top_of_screen + 4, buf);
949 #endif /* CONFIG_CODEC != SWCODEC */
951 FOR_NB_SCREENS(i)
953 screens[i].update_viewport();
954 screens[i].set_viewport(NULL);
957 #ifdef HAVE_BUTTONBAR
958 gui_buttonbar_draw(&buttonbar);
959 #endif
961 /* Only force the redraw if update_screen is true */
962 gui_syncstatusbar_draw(&statusbars,true);
965 update_screen = false;
967 if (global_settings.talk_file && talk
968 && radio_status == FMRADIO_PAUSED)
970 talk = false;
971 bool enqueue = false;
972 if (radio_mode == RADIO_SCAN_MODE)
974 talk_freq(curr_freq, enqueue);
975 enqueue = true;
977 if (curr_preset >= 0)
978 talk_preset(curr_preset, radio_mode == RADIO_PRESET_MODE,
979 enqueue);
982 #if CONFIG_CODEC != SWCODEC
983 if(audio_status() & AUDIO_STATUS_ERROR)
985 done = true;
987 #endif
989 #ifndef HAVE_NOISY_IDLE_MODE
990 if (TIME_AFTER(current_tick, button_timeout))
992 cpu_idle_mode(true);
994 #endif
995 } /*while(!done)*/
997 #ifndef SIMULATOR
998 #if CONFIG_CODEC != SWCODEC
999 if(audio_status() & AUDIO_STATUS_ERROR)
1001 gui_syncsplash(0, str(LANG_DISK_FULL));
1002 gui_syncstatusbar_draw(&statusbars,true);
1003 FOR_NB_SCREENS(i)
1005 screens[i].set_viewport(&vp[i]);
1006 screens[i].update_viewport();
1007 screens[i].set_viewport(NULL);
1009 audio_error_clear();
1011 while(1)
1013 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
1014 if(button == ACTION_FM_STOP)
1015 break;
1019 audio_init_playback();
1020 #endif /* CONFIG_CODEC != SWCODEC */
1022 sound_settings_apply();
1023 #endif /* SIMULATOR */
1025 if(keep_playing)
1027 /* Catch FMRADIO_PLAYING status for the sim. */
1028 #ifndef SIMULATOR
1029 #if CONFIG_CODEC != SWCODEC
1030 /* Enable the Left and right A/D Converter */
1031 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
1032 sound_default(SOUND_RIGHT_GAIN),
1033 AUDIO_GAIN_LINEIN);
1034 mas_codec_writereg(6, 0x4000);
1035 #endif
1036 end_search();
1037 #endif /* SIMULATOR */
1039 else
1041 #if CONFIG_CODEC == SWCODEC
1042 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
1043 #else
1044 radio_stop();
1045 #endif
1048 #ifndef HAVE_NOISY_IDLE_MODE
1049 cpu_idle_mode(false);
1050 #endif
1052 /* restore status bar settings */
1053 global_settings.statusbar = statusbar;
1054 global_status.statusbar_forced = 0;
1055 in_screen = false;
1056 #if CONFIG_CODEC != SWCODEC
1057 return have_recorded;
1058 #else
1059 return false;
1060 #endif
1061 } /* radio_screen */
1063 static void radio_save_presets(void)
1065 int fd;
1066 int i;
1068 fd = creat(filepreset);
1069 if(fd >= 0)
1071 for(i = 0;i < num_presets;i++)
1073 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
1075 close(fd);
1077 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
1078 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
1079 presets_changed = false;
1081 else
1083 gui_syncsplash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED));
1087 void radio_load_presets(char *filename)
1089 int fd;
1090 int rc;
1091 char buf[128];
1092 char *freq;
1093 char *name;
1094 bool done = false;
1095 int f;
1097 memset(presets, 0, sizeof(presets));
1098 num_presets = 0;
1100 /* No Preset in configuration. */
1101 if(filename[0] == '\0')
1103 filepreset[0] = '\0';
1104 return;
1106 /* Temporary preset, loaded until player shuts down. */
1107 else if(filename[0] == '/')
1108 strncpy(filepreset, filename, sizeof(filepreset));
1109 /* Preset from default directory. */
1110 else
1111 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1112 FMPRESET_PATH, filename);
1114 fd = open(filepreset, O_RDONLY);
1115 if(fd >= 0)
1117 while(!done && num_presets < MAX_PRESETS)
1119 rc = read_line(fd, buf, 128);
1120 if(rc > 0)
1122 if(settings_parseline(buf, &freq, &name))
1124 f = atoi(freq);
1125 if(f) /* For backwards compatibility */
1127 struct fmstation * const fms = &presets[num_presets];
1128 fms->frequency = f;
1129 strncpy(fms->name, name, MAX_FMPRESET_LEN);
1130 fms->name[MAX_FMPRESET_LEN] = '\0';
1131 num_presets++;
1135 else
1136 done = true;
1138 close(fd);
1140 else /* invalid file name? */
1141 filepreset[0] = '\0';
1143 presets_loaded = num_presets > 0;
1144 presets_changed = false;
1148 static int radio_add_preset(void)
1150 char buf[MAX_FMPRESET_LEN + 1];
1152 if(num_presets < MAX_PRESETS)
1154 memset(buf, 0, MAX_FMPRESET_LEN);
1156 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1158 struct fmstation * const fms = &presets[num_presets];
1159 buf[MAX_FMPRESET_LEN] = '\0';
1160 strcpy(fms->name, buf);
1161 fms->frequency = curr_freq;
1162 num_presets++;
1163 presets_changed = true;
1164 presets_loaded = num_presets > 0;
1167 else
1169 gui_syncsplash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS));
1171 return true;
1174 /* needed to know which preset we are edit/delete-ing */
1175 static int selected_preset = -1;
1176 static int radio_edit_preset(void)
1178 char buf[MAX_FMPRESET_LEN + 1];
1180 if (num_presets > 0)
1182 struct fmstation * const fms = &presets[selected_preset];
1184 strncpy(buf, fms->name, MAX_FMPRESET_LEN);
1186 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1188 buf[MAX_FMPRESET_LEN] = '\0';
1189 strcpy(fms->name, buf);
1190 presets_changed = true;
1194 return 1;
1197 static int radio_delete_preset(void)
1199 if (num_presets > 0)
1201 struct fmstation * const fms = &presets[selected_preset];
1203 if (selected_preset >= --num_presets)
1204 selected_preset = num_presets - 1;
1206 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1207 (uintptr_t)fms);
1211 /* Don't ask to save when all presets are deleted. */
1212 presets_changed = num_presets > 0;
1214 if (!presets_changed)
1216 /* The preset list will be cleared, switch to Scan Mode. */
1217 radio_mode = RADIO_SCAN_MODE;
1218 presets_loaded = false;
1221 return 1;
1224 static int load_preset_list(void)
1226 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1229 static int save_preset_list(void)
1231 if(num_presets > 0)
1233 bool bad_file_name = true;
1235 if(!dir_exists(FMPRESET_PATH)) /* Check if there is preset folder */
1236 mkdir(FMPRESET_PATH);
1238 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1239 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1241 while(bad_file_name)
1243 if(!kbd_input(filepreset, sizeof(filepreset)))
1245 /* check the name: max MAX_FILENAME (20) chars */
1246 char* p2;
1247 char* p1;
1248 int len;
1249 p1 = strrchr(filepreset, '/');
1250 p2 = p1;
1251 while((p1) && (*p2) && (*p2 != '.'))
1252 p2++;
1253 len = (int)(p2-p1) - 1;
1254 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1256 /* no slash, too long or too short */
1257 gui_syncsplash(HZ, ID2P(LANG_INVALID_FILENAME));
1259 else
1261 /* add correct extension (easier to always write)
1262 at this point, p2 points to 0 or the extension dot */
1263 *p2 = '\0';
1264 strcat(filepreset,".fmr");
1265 bad_file_name = false;
1266 radio_save_presets();
1269 else
1271 /* user aborted */
1272 return false;
1276 else
1277 gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS));
1279 return true;
1282 static int clear_preset_list(void)
1284 /* Clear all the preset entries */
1285 memset(presets, 0, sizeof (presets));
1287 num_presets = 0;
1288 presets_loaded = false;
1289 /* The preset list will be cleared switch to Scan Mode. */
1290 radio_mode = RADIO_SCAN_MODE;
1292 presets_changed = false; /* Don't ask to save when clearing the list. */
1294 return true;
1297 MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1298 ID2P(LANG_FM_EDIT_PRESET),
1299 radio_edit_preset, NULL, NULL, Icon_NOICON);
1300 MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1301 ID2P(LANG_FM_DELETE_PRESET),
1302 radio_delete_preset, NULL, NULL, Icon_NOICON);
1303 static int radio_preset_callback(int action,
1304 const struct menu_item_ex *this_item)
1306 if (action == ACTION_STD_OK)
1307 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1308 return action;
1309 (void)this_item;
1311 MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_PRESET),
1312 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1313 &radio_delete_preset_item);
1314 /* present a list of preset stations */
1315 static char * presets_get_name(int selected_item, void *data,
1316 char *buffer, size_t buffer_len)
1318 (void)data;
1319 struct fmstation *p = &presets[selected_item];
1320 if(p->name[0])
1321 return p->name;
1322 int freq = p->frequency / 10000;
1323 int frac = freq % 100;
1324 freq /= 100;
1325 snprintf(buffer, buffer_len,
1326 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1327 return buffer;
1330 static int presets_speak_name(int selected_item, void * data)
1332 (void)data;
1333 talk_preset(selected_item, true, false);
1334 return 0;
1337 static int handle_radio_presets(void)
1339 struct gui_synclist lists;
1340 int result = 0;
1341 int action = ACTION_NONE;
1342 #ifdef HAVE_BUTTONBAR
1343 struct gui_buttonbar buttonbar;
1344 #endif
1346 if(presets_loaded == false)
1347 return result;
1349 #ifdef HAVE_BUTTONBAR
1350 gui_buttonbar_init(&buttonbar);
1351 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1352 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1353 str(LANG_FM_BUTTONBAR_EXIT),
1354 str(LANG_FM_BUTTONBAR_ACTION));
1355 gui_buttonbar_draw(&buttonbar);
1356 #endif
1357 gui_synclist_init(&lists, presets_get_name, NULL, false, 1, NULL);
1358 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
1359 gui_synclist_set_icon_callback(&lists, NULL);
1360 if(global_settings.talk_file)
1361 gui_synclist_set_voice_callback(&lists, presets_speak_name);
1362 gui_synclist_set_nb_items(&lists, num_presets);
1363 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1364 gui_synclist_speak_item(&lists);
1366 while (result == 0)
1368 gui_synclist_draw(&lists);
1369 gui_syncstatusbar_draw(&statusbars, true);
1370 list_do_action(CONTEXT_STD, HZ,
1371 &lists, &action, LIST_WRAP_UNLESS_HELD);
1372 switch (action)
1374 case ACTION_STD_MENU:
1375 radio_add_preset();
1376 break;
1377 case ACTION_STD_CANCEL:
1378 result = 1;
1379 break;
1380 case ACTION_STD_OK:
1381 curr_preset = gui_synclist_get_sel_pos(&lists);
1382 curr_freq = presets[curr_preset].frequency;
1383 next_station(0);
1384 remember_frequency();
1385 result = 1;
1386 break;
1387 case ACTION_F3:
1388 case ACTION_STD_CONTEXT:
1389 selected_preset = gui_synclist_get_sel_pos(&lists);
1390 do_menu(&handle_radio_preset_menu, NULL, NULL, false);
1391 gui_synclist_speak_item(&lists);
1392 break;
1393 default:
1394 if(default_event_handler(action) == SYS_USB_CONNECTED)
1395 result = 2;
1398 return result - 1;
1401 void toggle_mono_mode(bool mono)
1403 tuner_set(RADIO_FORCE_MONO, mono);
1406 void set_radio_region(int region)
1408 #ifdef HAVE_RADIO_REGION
1409 tuner_set(RADIO_REGION, region);
1410 #endif
1411 next_station(0);
1412 remember_frequency();
1413 (void)region;
1416 MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
1417 MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
1419 #ifndef FM_MODE
1420 static char* get_mode_text(int selected_item, void * data, char *buffer)
1422 (void)selected_item;
1423 (void)data;
1424 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_MODE),
1425 radio_mode ? str(LANG_PRESET) :
1426 str(LANG_RADIO_SCAN_MODE));
1427 return buffer;
1429 static int toggle_radio_mode(void)
1431 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
1432 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
1433 return 0;
1435 MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1436 toggle_radio_mode, NULL,
1437 get_mode_text, NULL, NULL, NULL, Icon_NOICON);
1438 #endif
1440 static int scan_presets(void *viewports)
1442 bool do_scan = true;
1443 int i;
1444 struct viewport *vp = (struct viewport *)viewports;
1446 FOR_NB_SCREENS(i)
1447 screens[i].set_viewport(vp?&vp[i]:NULL);
1448 if(num_presets > 0) /* Do that to avoid 2 questions. */
1449 do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS));
1451 if(do_scan)
1453 const struct fm_region_data * const fmr =
1454 &fm_region_data[global_settings.fm_region];
1456 char buf[MAX_FMPRESET_LEN + 1];
1458 curr_freq = fmr->freq_min;
1459 num_presets = 0;
1460 memset(presets, 0, sizeof(presets));
1461 tuner_set(RADIO_MUTE, 1);
1463 while(curr_freq <= fmr->freq_max)
1465 int freq, frac;
1466 if (num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1467 break;
1469 freq = curr_freq / 10000;
1470 frac = freq % 100;
1471 freq /= 100;
1473 snprintf(buf, MAX_FMPRESET_LEN, str(LANG_FM_SCANNING), freq, frac);
1474 gui_syncsplash(0, buf);
1476 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
1478 /* add preset */
1479 presets[num_presets].name[0] = '\0';
1480 presets[num_presets].frequency = curr_freq;
1481 num_presets++;
1484 curr_freq += fmr->freq_step;
1487 if (radio_status == FMRADIO_PLAYING)
1488 tuner_set(RADIO_MUTE, 0);
1490 presets_changed = true;
1492 FOR_NB_SCREENS(i)
1494 screens[i].clear_viewport();
1495 screens[i].update_viewport();
1498 if(num_presets > 0)
1500 curr_freq = presets[0].frequency;
1501 radio_mode = RADIO_PRESET_MODE;
1502 presets_loaded = true;
1503 next_station(0);
1505 else
1507 /* Wrap it to beginning or we'll be past end of band */
1508 presets_loaded = false;
1509 next_station(1);
1512 return true;
1516 #ifdef HAVE_RECORDING
1518 #if defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC
1519 #define FM_RECORDING_SCREEN
1520 static int fm_recording_screen(void)
1522 bool ret;
1524 /* switch recording source to FMRADIO for the duration */
1525 int rec_source = global_settings.rec_source;
1526 global_settings.rec_source = AUDIO_SRC_FMRADIO;
1528 ret = recording_screen(true);
1530 /* safe to reset as changing sources is prohibited here */
1531 global_settings.rec_source = rec_source;
1533 return ret;
1536 #endif /* defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC */
1538 #if defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC
1539 #define FM_RECORDING_SETTINGS
1540 static int fm_recording_settings(void)
1542 bool ret = recording_menu(true);
1544 #if CONFIG_CODEC != SWCODEC
1545 if (!ret)
1547 struct audio_recording_options rec_options;
1548 rec_init_recording_options(&rec_options);
1549 rec_options.rec_source = AUDIO_SRC_LINEIN;
1550 rec_set_recording_options(&rec_options);
1552 #endif
1554 return ret;
1557 #endif /* defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC */
1558 #endif /* HAVE_RECORDING */
1560 #ifdef FM_RECORDING_SCREEN
1561 MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING),
1562 fm_recording_screen, NULL, NULL, Icon_Recording);
1563 #endif
1564 #ifdef FM_RECORDING_SETTINGS
1565 MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1566 fm_recording_settings, NULL, NULL, Icon_Recording);
1567 #endif
1568 #ifndef FM_PRESET
1569 MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
1570 handle_radio_presets, NULL, NULL, Icon_NOICON);
1571 #endif
1572 #ifndef FM_PRESET_ADD
1573 MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1574 radio_add_preset, NULL, NULL, Icon_NOICON);
1575 #endif
1578 MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1579 load_preset_list, NULL, NULL, Icon_NOICON);
1580 MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1581 save_preset_list, NULL, NULL, Icon_NOICON);
1582 MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1583 clear_preset_list, NULL, NULL, Icon_NOICON);
1584 MENUITEM_FUNCTION(scan_presets_item, MENU_FUNC_USEPARAM,
1585 ID2P(LANG_FM_SCAN_PRESETS),
1586 scan_presets, NULL, NULL, Icon_NOICON);
1588 MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
1589 Icon_Radio_screen,
1590 #ifndef FM_PRESET
1591 &radio_presets_item,
1592 #endif
1593 #ifndef FM_PRESET_ADD
1594 &radio_addpreset_item,
1595 #endif
1596 &presetload_item, &presetsave_item, &presetclear_item,
1597 &force_mono,
1598 #ifndef FM_MODE
1599 &radio_mode_item,
1600 #endif
1601 &set_region, &sound_settings,
1602 #ifdef FM_RECORDING_SCREEN
1603 &recscreen_item,
1604 #endif
1605 #ifdef FM_RECORDING_SETTINGS
1606 &recsettings_item,
1607 #endif
1608 &scan_presets_item);
1609 /* main menu of the radio screen */
1610 static bool radio_menu(void)
1612 return do_menu(&radio_settings_menu, NULL, NULL, false) == MENU_ATTACHED_USB;
1615 #endif