Don't objcopy simulator plugins.
[kugel-rb.git] / apps / recorder / radio.c
blob5f9fff8d640328ce77522fd0fc549a0089f19a28
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 firmware
168 and clean up the 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 * fmr->freq_step + fmr->freq_min;
191 tuner_set(RADIO_SLEEP, 0); /* wake up the tuner */
193 if(radio_status == FMRADIO_OFF)
195 #ifdef HAVE_RADIO_REGION
196 tuner_set(RADIO_REGION, global_settings.fm_region);
197 #endif
198 tuner_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
201 tuner_set(RADIO_FREQUENCY, curr_freq);
203 #ifdef HAVE_RADIO_MUTE_TIMEOUT
205 unsigned long mute_timeout = current_tick + HZ;
206 if (radio_status != FMRADIO_OFF)
208 /* paused */
209 mute_timeout += HZ;
212 while(!tuner_get(RADIO_STEREO) && !tuner_get(RADIO_TUNED))
214 if(TIME_AFTER(current_tick, mute_timeout))
215 break;
216 yield();
219 #endif
221 /* keep radio from sounding initially */
222 if(!start_paused)
223 tuner_set(RADIO_MUTE, 0);
225 radio_status = FMRADIO_PLAYING;
226 } /* radio_start */
228 void radio_pause(void)
230 if(radio_status == FMRADIO_PAUSED)
231 return;
233 if(radio_status == FMRADIO_OFF)
235 radio_status |= FMRADIO_START_PAUSED;
236 radio_start();
239 tuner_set(RADIO_MUTE, 1);
240 tuner_set(RADIO_SLEEP, 1);
242 radio_status = FMRADIO_PAUSED;
243 } /* radio_pause */
245 void radio_stop(void)
247 if(radio_status == FMRADIO_OFF)
248 return;
250 tuner_set(RADIO_MUTE, 1);
251 tuner_set(RADIO_SLEEP, 1); /* low power mode, if available */
252 radio_status = FMRADIO_OFF;
253 tuner_power(false); /* status update, power off if avail. */
254 } /* radio_stop */
256 bool radio_hardware_present(void)
258 return tuner_get(RADIO_PRESENT);
261 /* Keep freq on the grid for the current region */
262 static int snap_freq_to_grid(int freq)
264 const struct fm_region_data * const fmr =
265 &fm_region_data[global_settings.fm_region];
267 /* Range clamp if out of range or just round to nearest */
268 if (freq < fmr->freq_min)
269 freq = fmr->freq_min;
270 else if (freq > fmr->freq_max)
271 freq = fmr->freq_max;
272 else
273 freq = (freq - fmr->freq_min + fmr->freq_step/2) /
274 fmr->freq_step * fmr->freq_step + fmr->freq_min;
276 return freq;
279 /* Find a matching preset to freq */
280 static int find_preset(int freq)
282 int i;
283 if(num_presets < 1)
284 return -1;
285 for(i = 0;i < MAX_PRESETS;i++)
287 if(freq == presets[i].frequency)
288 return i;
291 return -1;
294 /* Return the first preset encountered in the search direction with
295 wraparound. */
296 static int find_closest_preset(int freq, int direction)
298 int i;
300 if (direction == 0) /* direction == 0 isn't really used */
301 return 0;
303 for (i = 0; i < MAX_PRESETS; i++)
305 int preset_frequency = presets[i].frequency;
307 if (preset_frequency == freq)
308 return i; /* Exact match = stop */
309 /* Stop when the preset frequency exeeds freq so that we can
310 pick the correct one based on direction */
311 if (preset_frequency > freq)
312 break;
315 /* wrap around depending on direction */
316 if (i == 0 || i >= num_presets - 1)
317 i = direction < 0 ? num_presets - 1 : 0;
318 else if (direction < 0)
319 i--; /* use previous */
321 return i;
324 static void remember_frequency(void)
326 const struct fm_region_data * const fmr =
327 &fm_region_data[global_settings.fm_region];
328 global_status.last_frequency = (curr_freq - fmr->freq_min)
329 / fmr->freq_step;
330 status_save();
333 static void next_preset(int direction)
335 if (num_presets < 1)
336 return;
338 if (curr_preset == -1)
339 curr_preset = find_closest_preset(curr_freq, direction);
340 else
341 curr_preset = (curr_preset + direction + num_presets) % num_presets;
343 /* Must stay on the current grid for the region */
344 curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
346 tuner_set(RADIO_FREQUENCY, curr_freq);
347 remember_frequency();
350 /* Step to the next or previous frequency */
351 static int step_freq(int freq, int direction)
353 const struct fm_region_data * const fmr =
354 &fm_region_data[global_settings.fm_region];
356 freq += direction*fmr->freq_step;
358 /* Wrap first or snapping to grid will not let us on the band extremes */
359 if (freq > fmr->freq_max)
360 freq = direction > 0 ? fmr->freq_min : fmr->freq_max;
361 else if (freq < fmr->freq_min)
362 freq = direction < 0 ? fmr->freq_max : fmr->freq_min;
363 else
364 freq = snap_freq_to_grid(freq);
366 return freq;
369 /* Step to the next or previous station */
370 static void next_station(int direction)
372 if (direction != 0 && radio_mode != RADIO_SCAN_MODE)
374 next_preset(direction);
375 return;
378 curr_freq = step_freq(curr_freq, direction);
380 if (radio_status == FMRADIO_PLAYING)
381 tuner_set(RADIO_MUTE, 1);
383 tuner_set(RADIO_FREQUENCY, curr_freq);
385 if (radio_status == FMRADIO_PLAYING)
386 tuner_set(RADIO_MUTE, 0);
388 curr_preset = find_preset(curr_freq);
389 remember_frequency();
392 /* Ends an in-progress search */
393 static void end_search(void)
395 if (search_dir != 0 && radio_status == FMRADIO_PLAYING)
396 tuner_set(RADIO_MUTE, 0);
397 search_dir = 0;
400 /* Speak a frequency. */
401 static void talk_freq(int freq, bool enqueue)
403 freq /= 10000;
404 talk_number(freq / 100, enqueue);
405 talk_id(LANG_POINT, true);
406 talk_number(freq % 100 / 10, true);
407 if (freq % 10)
408 talk_number(freq % 10, true);
411 /* Speak a preset by number or by spelling its name, depending on settings. */
412 static void talk_preset(int preset, bool fallback, bool enqueue)
414 if (global_settings.talk_file == 1) /* number */
415 talk_number(preset + 1, enqueue);
416 else
417 { /* spell */
418 if(presets[preset].name[0])
419 talk_spell(presets[preset].name, enqueue);
420 else if(fallback)
421 talk_freq(presets[preset].frequency, enqueue);
425 int radio_screen(void)
427 char buf[MAX_PATH];
428 bool done = false;
429 int ret_val = GO_TO_ROOT;
430 int button;
431 int i;
432 bool stereo = false, last_stereo = false;
433 int fh;
434 int top_of_screen = 0;
435 bool update_screen = true;
436 bool screen_freeze = false;
437 bool keep_playing = false;
438 bool statusbar = global_settings.statusbar;
439 bool talk = false;
440 #ifdef FM_RECORD_DBLPRE
441 int lastbutton = BUTTON_NONE;
442 unsigned long rec_lastclick = 0;
443 #endif
444 #if CONFIG_CODEC != SWCODEC
445 bool have_recorded = false;
446 int timeout = current_tick + HZ/10;
447 unsigned int seconds = 0;
448 unsigned int last_seconds = 0;
449 int hours, minutes;
450 struct audio_recording_options rec_options;
451 #endif /* CONFIG_CODEC != SWCODEC */
452 #ifndef HAVE_NOISY_IDLE_MODE
453 int button_timeout = current_tick + (2*HZ);
454 #endif
455 struct viewport vp[NB_SCREENS];
456 #ifdef HAVE_BUTTONBAR
457 struct gui_buttonbar buttonbar;
458 gui_buttonbar_init(&buttonbar);
459 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
460 #endif
462 /* change status to "in screen" */
463 in_screen = true;
465 /* always display status bar in radio screen for now */
466 global_status.statusbar_forced = statusbar?0:1;
467 global_settings.statusbar = true;
468 gui_syncstatusbar_draw(&statusbars,true);
469 FOR_NB_SCREENS(i)
471 viewport_set_defaults(&vp[i], i);
472 #ifdef HAVE_BUTTONBAR
473 if (global_settings.buttonbar)
474 vp[i].height -= BUTTONBAR_HEIGHT;
475 #endif
476 screens[i].set_viewport(&vp[i]);
477 screens[i].stop_scroll();
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 splash(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 splash(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),
863 fh);
864 screens[i].update_rect(0,
865 STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
866 screens[i].getwidth(), fh);
867 screens[i].set_viewport(NULL);
871 if(TIME_AFTER(current_tick, timeout))
873 timeout = current_tick + HZ;
874 #else /* SWCODEC */
876 #endif /* CONFIG_CODEC == SWCODEC */
878 /* keep "mono" from always being displayed when paused */
879 if (radio_status != FMRADIO_PAUSED)
881 stereo = tuner_get(RADIO_STEREO) &&
882 !global_settings.fm_force_mono;
884 if(stereo != last_stereo)
886 update_screen = true;
887 last_stereo = stereo;
892 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
893 seconds = audio_recorded_time() / HZ;
894 if (update_screen || seconds > last_seconds)
896 last_seconds = seconds;
897 #else
898 if (update_screen)
900 #endif
901 int freq;
903 FOR_NB_SCREENS(i)
905 screens[i].set_viewport(&vp[i]);
908 snprintf(buf, 128, curr_preset >= 0 ? "%d. %s" : " ",
909 curr_preset + 1, presets[curr_preset].name);
911 FOR_NB_SCREENS(i)
912 screens[i].puts_scroll(0, top_of_screen, buf);
914 freq = curr_freq / 10000;
915 snprintf(buf, 128, str(LANG_FM_STATION),
916 freq / 100, freq % 100);
917 FOR_NB_SCREENS(i)
918 screens[i].puts_scroll(0, top_of_screen + 1, buf);
920 snprintf(buf, 128, "%s", stereo?str(LANG_CHANNEL_STEREO):
921 str(LANG_CHANNEL_MONO));
922 FOR_NB_SCREENS(i)
923 screens[i].puts_scroll(0, top_of_screen + 2, buf);
925 snprintf(buf, 128, "%s %s", str(LANG_MODE),
926 radio_mode ? str(LANG_PRESET) :
927 str(LANG_RADIO_SCAN_MODE));
928 FOR_NB_SCREENS(i)
929 screens[i].puts_scroll(0, top_of_screen + 3, buf);
931 #if CONFIG_CODEC != SWCODEC
932 if(audio_status() == AUDIO_STATUS_RECORD)
934 hours = seconds / 3600;
935 minutes = (seconds - (hours * 3600)) / 60;
936 snprintf(buf, 32, "%s %02d:%02d:%02d",
937 str(LANG_RECORDING_TIME),
938 hours, minutes, seconds%60);
939 FOR_NB_SCREENS(i)
940 screens[i].puts_scroll(0, top_of_screen + 4, buf);
942 else
944 if(rec_options.rec_prerecord_time)
946 snprintf(buf, 32, "%s %02d",
947 str(LANG_RECORD_PRERECORD), seconds%60);
948 FOR_NB_SCREENS(i)
949 screens[i].puts_scroll(0, top_of_screen + 4, buf);
952 #endif /* CONFIG_CODEC != SWCODEC */
954 FOR_NB_SCREENS(i)
956 screens[i].update_viewport();
957 screens[i].set_viewport(NULL);
960 #ifdef HAVE_BUTTONBAR
961 gui_buttonbar_draw(&buttonbar);
962 #endif
964 /* Only force the redraw if update_screen is true */
965 gui_syncstatusbar_draw(&statusbars,true);
968 update_screen = false;
970 if (global_settings.talk_file && talk
971 && radio_status == FMRADIO_PAUSED)
973 talk = false;
974 bool enqueue = false;
975 if (radio_mode == RADIO_SCAN_MODE)
977 talk_freq(curr_freq, enqueue);
978 enqueue = true;
980 if (curr_preset >= 0)
981 talk_preset(curr_preset, radio_mode == RADIO_PRESET_MODE,
982 enqueue);
985 #if CONFIG_CODEC != SWCODEC
986 if(audio_status() & AUDIO_STATUS_ERROR)
988 done = true;
990 #endif
992 #ifndef HAVE_NOISY_IDLE_MODE
993 if (TIME_AFTER(current_tick, button_timeout))
995 cpu_idle_mode(true);
997 #endif
998 } /*while(!done)*/
1000 #ifndef SIMULATOR
1001 #if CONFIG_CODEC != SWCODEC
1002 if(audio_status() & AUDIO_STATUS_ERROR)
1004 splash(0, str(LANG_DISK_FULL));
1005 gui_syncstatusbar_draw(&statusbars,true);
1006 FOR_NB_SCREENS(i)
1008 screens[i].set_viewport(&vp[i]);
1009 screens[i].update_viewport();
1010 screens[i].set_viewport(NULL);
1012 audio_error_clear();
1014 while(1)
1016 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
1017 if(button == ACTION_FM_STOP)
1018 break;
1022 audio_init_playback();
1023 #endif /* CONFIG_CODEC != SWCODEC */
1025 sound_settings_apply();
1026 #endif /* SIMULATOR */
1028 if(keep_playing)
1030 /* Catch FMRADIO_PLAYING status for the sim. */
1031 #ifndef SIMULATOR
1032 #if CONFIG_CODEC != SWCODEC
1033 /* Enable the Left and right A/D Converter */
1034 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
1035 sound_default(SOUND_RIGHT_GAIN),
1036 AUDIO_GAIN_LINEIN);
1037 mas_codec_writereg(6, 0x4000);
1038 #endif
1039 end_search();
1040 #endif /* SIMULATOR */
1042 else
1044 #if CONFIG_CODEC == SWCODEC
1045 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
1046 #else
1047 radio_stop();
1048 #endif
1051 #ifndef HAVE_NOISY_IDLE_MODE
1052 cpu_idle_mode(false);
1053 #endif
1055 /* restore status bar settings */
1056 global_settings.statusbar = statusbar;
1057 global_status.statusbar_forced = 0;
1058 in_screen = false;
1059 #if CONFIG_CODEC != SWCODEC
1060 return have_recorded;
1061 #else
1062 return false;
1063 #endif
1064 } /* radio_screen */
1066 static void radio_save_presets(void)
1068 int fd;
1069 int i;
1071 fd = creat(filepreset);
1072 if(fd >= 0)
1074 for(i = 0;i < num_presets;i++)
1076 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
1078 close(fd);
1080 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
1081 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
1082 presets_changed = false;
1084 else
1086 splash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED));
1090 void radio_load_presets(char *filename)
1092 int fd;
1093 int rc;
1094 char buf[128];
1095 char *freq;
1096 char *name;
1097 bool done = false;
1098 int f;
1100 memset(presets, 0, sizeof(presets));
1101 num_presets = 0;
1103 /* No Preset in configuration. */
1104 if(filename[0] == '\0')
1106 filepreset[0] = '\0';
1107 return;
1109 /* Temporary preset, loaded until player shuts down. */
1110 else if(filename[0] == '/')
1111 strncpy(filepreset, filename, sizeof(filepreset));
1112 /* Preset from default directory. */
1113 else
1114 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1115 FMPRESET_PATH, filename);
1117 fd = open_utf8(filepreset, O_RDONLY);
1118 if(fd >= 0)
1120 while(!done && num_presets < MAX_PRESETS)
1122 rc = read_line(fd, buf, 128);
1123 if(rc > 0)
1125 if(settings_parseline(buf, &freq, &name))
1127 f = atoi(freq);
1128 if(f) /* For backwards compatibility */
1130 struct fmstation * const fms = &presets[num_presets];
1131 fms->frequency = f;
1132 strncpy(fms->name, name, MAX_FMPRESET_LEN);
1133 fms->name[MAX_FMPRESET_LEN] = '\0';
1134 num_presets++;
1138 else
1139 done = true;
1141 close(fd);
1143 else /* invalid file name? */
1144 filepreset[0] = '\0';
1146 presets_loaded = num_presets > 0;
1147 presets_changed = false;
1151 static int radio_add_preset(void)
1153 char buf[MAX_FMPRESET_LEN + 1];
1155 if(num_presets < MAX_PRESETS)
1157 memset(buf, 0, MAX_FMPRESET_LEN);
1159 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1161 struct fmstation * const fms = &presets[num_presets];
1162 buf[MAX_FMPRESET_LEN] = '\0';
1163 strcpy(fms->name, buf);
1164 fms->frequency = curr_freq;
1165 num_presets++;
1166 presets_changed = true;
1167 presets_loaded = num_presets > 0;
1170 else
1172 splash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS));
1174 return true;
1177 /* needed to know which preset we are edit/delete-ing */
1178 static int selected_preset = -1;
1179 static int radio_edit_preset(void)
1181 char buf[MAX_FMPRESET_LEN + 1];
1183 if (num_presets > 0)
1185 struct fmstation * const fms = &presets[selected_preset];
1187 strncpy(buf, fms->name, MAX_FMPRESET_LEN);
1189 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1191 buf[MAX_FMPRESET_LEN] = '\0';
1192 strcpy(fms->name, buf);
1193 presets_changed = true;
1197 return 1;
1200 static int radio_delete_preset(void)
1202 if (num_presets > 0)
1204 struct fmstation * const fms = &presets[selected_preset];
1206 if (selected_preset >= --num_presets)
1207 selected_preset = num_presets - 1;
1209 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1210 (uintptr_t)fms);
1214 /* Don't ask to save when all presets are deleted. */
1215 presets_changed = num_presets > 0;
1217 if (!presets_changed)
1219 /* The preset list will be cleared, switch to Scan Mode. */
1220 radio_mode = RADIO_SCAN_MODE;
1221 presets_loaded = false;
1224 return 1;
1227 static int load_preset_list(void)
1229 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1232 static int save_preset_list(void)
1234 if(num_presets > 0)
1236 bool bad_file_name = true;
1238 if(!dir_exists(FMPRESET_PATH)) /* Check if there is preset folder */
1239 mkdir(FMPRESET_PATH);
1241 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1242 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1244 while(bad_file_name)
1246 if(!kbd_input(filepreset, sizeof(filepreset)))
1248 /* check the name: max MAX_FILENAME (20) chars */
1249 char* p2;
1250 char* p1;
1251 int len;
1252 p1 = strrchr(filepreset, '/');
1253 p2 = p1;
1254 while((p1) && (*p2) && (*p2 != '.'))
1255 p2++;
1256 len = (int)(p2-p1) - 1;
1257 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1259 /* no slash, too long or too short */
1260 splash(HZ, ID2P(LANG_INVALID_FILENAME));
1262 else
1264 /* add correct extension (easier to always write)
1265 at this point, p2 points to 0 or the extension dot */
1266 *p2 = '\0';
1267 strcat(filepreset,".fmr");
1268 bad_file_name = false;
1269 radio_save_presets();
1272 else
1274 /* user aborted */
1275 return false;
1279 else
1280 splash(HZ, ID2P(LANG_FM_NO_PRESETS));
1282 return true;
1285 static int clear_preset_list(void)
1287 /* Clear all the preset entries */
1288 memset(presets, 0, sizeof (presets));
1290 num_presets = 0;
1291 presets_loaded = false;
1292 /* The preset list will be cleared switch to Scan Mode. */
1293 radio_mode = RADIO_SCAN_MODE;
1295 presets_changed = false; /* Don't ask to save when clearing the list. */
1297 return true;
1300 MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1301 ID2P(LANG_FM_EDIT_PRESET),
1302 radio_edit_preset, NULL, NULL, Icon_NOICON);
1303 MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1304 ID2P(LANG_FM_DELETE_PRESET),
1305 radio_delete_preset, NULL, NULL, Icon_NOICON);
1306 static int radio_preset_callback(int action,
1307 const struct menu_item_ex *this_item)
1309 if (action == ACTION_STD_OK)
1310 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1311 return action;
1312 (void)this_item;
1314 MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_PRESET),
1315 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1316 &radio_delete_preset_item);
1317 /* present a list of preset stations */
1318 static char * presets_get_name(int selected_item, void *data,
1319 char *buffer, size_t buffer_len)
1321 (void)data;
1322 struct fmstation *p = &presets[selected_item];
1323 if(p->name[0])
1324 return p->name;
1325 int freq = p->frequency / 10000;
1326 int frac = freq % 100;
1327 freq /= 100;
1328 snprintf(buffer, buffer_len,
1329 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1330 return buffer;
1333 static int presets_speak_name(int selected_item, void * data)
1335 (void)data;
1336 talk_preset(selected_item, true, false);
1337 return 0;
1340 static int handle_radio_presets(void)
1342 struct gui_synclist lists;
1343 int result = 0;
1344 int action = ACTION_NONE;
1345 #ifdef HAVE_BUTTONBAR
1346 struct gui_buttonbar buttonbar;
1347 #endif
1349 if(presets_loaded == false)
1350 return result;
1352 #ifdef HAVE_BUTTONBAR
1353 gui_buttonbar_init(&buttonbar);
1354 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1355 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1356 str(LANG_FM_BUTTONBAR_EXIT),
1357 str(LANG_FM_BUTTONBAR_ACTION));
1358 gui_buttonbar_draw(&buttonbar);
1359 #endif
1360 gui_synclist_init(&lists, presets_get_name, NULL, false, 1, NULL);
1361 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
1362 gui_synclist_set_icon_callback(&lists, NULL);
1363 if(global_settings.talk_file)
1364 gui_synclist_set_voice_callback(&lists, presets_speak_name);
1365 gui_synclist_set_nb_items(&lists, num_presets);
1366 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1367 gui_synclist_speak_item(&lists);
1369 while (result == 0)
1371 gui_synclist_draw(&lists);
1372 gui_syncstatusbar_draw(&statusbars, true);
1373 list_do_action(CONTEXT_STD, HZ,
1374 &lists, &action, LIST_WRAP_UNLESS_HELD);
1375 switch (action)
1377 case ACTION_STD_MENU:
1378 radio_add_preset();
1379 break;
1380 case ACTION_STD_CANCEL:
1381 result = 1;
1382 break;
1383 case ACTION_STD_OK:
1384 curr_preset = gui_synclist_get_sel_pos(&lists);
1385 curr_freq = presets[curr_preset].frequency;
1386 next_station(0);
1387 remember_frequency();
1388 result = 1;
1389 break;
1390 case ACTION_F3:
1391 case ACTION_STD_CONTEXT:
1392 selected_preset = gui_synclist_get_sel_pos(&lists);
1393 do_menu(&handle_radio_preset_menu, NULL, NULL, false);
1394 gui_synclist_set_nb_items(&lists, num_presets);
1395 gui_synclist_select_item(&lists, selected_preset);
1396 gui_synclist_speak_item(&lists);
1397 break;
1398 default:
1399 if(default_event_handler(action) == SYS_USB_CONNECTED)
1400 result = 2;
1403 return result - 1;
1406 void toggle_mono_mode(bool mono)
1408 tuner_set(RADIO_FORCE_MONO, mono);
1411 void set_radio_region(int region)
1413 #ifdef HAVE_RADIO_REGION
1414 tuner_set(RADIO_REGION, region);
1415 #endif
1416 next_station(0);
1417 remember_frequency();
1418 (void)region;
1421 MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
1422 MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
1424 #ifndef FM_MODE
1425 static char* get_mode_text(int selected_item, void * data, char *buffer)
1427 (void)selected_item;
1428 (void)data;
1429 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_MODE),
1430 radio_mode ? str(LANG_PRESET) :
1431 str(LANG_RADIO_SCAN_MODE));
1432 return buffer;
1434 static int toggle_radio_mode(void)
1436 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
1437 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
1438 return 0;
1440 MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1441 toggle_radio_mode, NULL,
1442 get_mode_text, NULL, NULL, NULL, Icon_NOICON);
1443 #endif
1445 static int scan_presets(void *viewports)
1447 bool do_scan = true;
1448 int i;
1449 struct viewport *vp = (struct viewport *)viewports;
1451 FOR_NB_SCREENS(i)
1452 screens[i].set_viewport(vp?&vp[i]:NULL);
1453 if(num_presets > 0) /* Do that to avoid 2 questions. */
1454 do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS));
1456 if(do_scan)
1458 const struct fm_region_data * const fmr =
1459 &fm_region_data[global_settings.fm_region];
1461 curr_freq = fmr->freq_min;
1462 num_presets = 0;
1463 memset(presets, 0, sizeof(presets));
1464 tuner_set(RADIO_MUTE, 1);
1466 while(curr_freq <= fmr->freq_max)
1468 int freq, frac;
1469 if(num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1470 break;
1472 freq = curr_freq / 10000;
1473 frac = freq % 100;
1474 freq /= 100;
1476 splashf(0, str(LANG_FM_SCANNING), freq, frac);
1478 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
1480 /* add preset */
1481 presets[num_presets].name[0] = '\0';
1482 presets[num_presets].frequency = curr_freq;
1483 num_presets++;
1486 curr_freq += fmr->freq_step;
1489 if (radio_status == FMRADIO_PLAYING)
1490 tuner_set(RADIO_MUTE, 0);
1492 presets_changed = true;
1494 FOR_NB_SCREENS(i)
1496 screens[i].clear_viewport();
1497 screens[i].update_viewport();
1500 if(num_presets > 0)
1502 curr_freq = presets[0].frequency;
1503 radio_mode = RADIO_PRESET_MODE;
1504 presets_loaded = true;
1505 next_station(0);
1507 else
1509 /* Wrap it to beginning or we'll be past end of band */
1510 presets_loaded = false;
1511 next_station(1);
1514 return true;
1518 #ifdef HAVE_RECORDING
1520 #if defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC
1521 #define FM_RECORDING_SCREEN
1522 static int fm_recording_screen(void)
1524 bool ret;
1526 /* switch recording source to FMRADIO for the duration */
1527 int rec_source = global_settings.rec_source;
1528 global_settings.rec_source = AUDIO_SRC_FMRADIO;
1530 ret = recording_screen(true);
1532 /* safe to reset as changing sources is prohibited here */
1533 global_settings.rec_source = rec_source;
1535 return ret;
1538 #endif /* defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC */
1540 #if defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC
1541 #define FM_RECORDING_SETTINGS
1542 static int fm_recording_settings(void)
1544 bool ret = recording_menu(true);
1546 #if CONFIG_CODEC != SWCODEC
1547 if (!ret)
1549 struct audio_recording_options rec_options;
1550 rec_init_recording_options(&rec_options);
1551 rec_options.rec_source = AUDIO_SRC_LINEIN;
1552 rec_set_recording_options(&rec_options);
1554 #endif
1556 return ret;
1559 #endif /* defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC */
1560 #endif /* HAVE_RECORDING */
1562 #ifdef FM_RECORDING_SCREEN
1563 MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING),
1564 fm_recording_screen, NULL, NULL, Icon_Recording);
1565 #endif
1566 #ifdef FM_RECORDING_SETTINGS
1567 MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1568 fm_recording_settings, NULL, NULL, Icon_Recording);
1569 #endif
1570 #ifndef FM_PRESET
1571 MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
1572 handle_radio_presets, NULL, NULL, Icon_NOICON);
1573 #endif
1574 #ifndef FM_PRESET_ADD
1575 MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1576 radio_add_preset, NULL, NULL, Icon_NOICON);
1577 #endif
1580 MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1581 load_preset_list, NULL, NULL, Icon_NOICON);
1582 MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1583 save_preset_list, NULL, NULL, Icon_NOICON);
1584 MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1585 clear_preset_list, NULL, NULL, Icon_NOICON);
1586 MENUITEM_FUNCTION(scan_presets_item, MENU_FUNC_USEPARAM,
1587 ID2P(LANG_FM_SCAN_PRESETS),
1588 scan_presets, NULL, NULL, Icon_NOICON);
1590 MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
1591 Icon_Radio_screen,
1592 #ifndef FM_PRESET
1593 &radio_presets_item,
1594 #endif
1595 #ifndef FM_PRESET_ADD
1596 &radio_addpreset_item,
1597 #endif
1598 &presetload_item, &presetsave_item, &presetclear_item,
1599 &force_mono,
1600 #ifndef FM_MODE
1601 &radio_mode_item,
1602 #endif
1603 &set_region, &sound_settings,
1604 #ifdef FM_RECORDING_SCREEN
1605 &recscreen_item,
1606 #endif
1607 #ifdef FM_RECORDING_SETTINGS
1608 &recsettings_item,
1609 #endif
1610 &scan_presets_item);
1611 /* main menu of the radio screen */
1612 static bool radio_menu(void)
1614 return do_menu(&radio_settings_menu, NULL, NULL, false) ==
1615 MENU_ATTACHED_USB;
1618 #endif