6e79adf302d8536e790924117d9470bfee71ecd2
[kugel-rb.git] / apps / recorder / radio.c
blob6e79adf302d8536e790924117d9470bfee71ecd2
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 "mas.h"
27 #include "settings.h"
28 #include "button.h"
29 #include "status.h"
30 #include "thread.h"
31 #include "audio.h"
32 #include "mp3_playback.h"
33 #include "ctype.h"
34 #include "file.h"
35 #include "general.h"
36 #include "errno.h"
37 #include "string-extra.h"
38 #include "system.h"
39 #include "radio.h"
40 #include "menu.h"
41 #include "misc.h"
42 #include "keyboard.h"
43 #include "screens.h"
44 #include "peakmeter.h"
45 #include "lang.h"
46 #include "font.h"
47 #include "sound_menu.h"
48 #ifdef HAVE_RECORDING
49 #include "recording.h"
50 #endif
51 #ifdef IPOD_ACCESSORY_PROTOCOL
52 #include "iap.h"
53 #endif
54 #include "appevents.h"
55 #include "talk.h"
56 #include "tuner.h"
57 #include "power.h"
58 #include "sound.h"
59 #include "screen_access.h"
60 #include "splash.h"
61 #include "yesno.h"
62 #include "buttonbar.h"
63 #include "tree.h"
64 #include "dir.h"
65 #include "action.h"
66 #include "list.h"
67 #include "menus/exported_menus.h"
68 #include "root_menu.h"
69 #include "viewport.h"
70 #include "skin_engine/skin_engine.h"
71 #include "statusbar-skinned.h"
73 #if CONFIG_TUNER
75 #if CONFIG_KEYPAD == RECORDER_PAD
76 #define FM_RECORD
77 #define FM_PRESET_ADD
78 #define FM_PRESET_ACTION
79 #define FM_PRESET
80 #define FM_MODE
82 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
83 #define FM_PRESET
84 #define FM_MODE
85 #define FM_NEXT_PRESET
86 #define FM_PREV_PRESET
88 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
89 #define FM_PRESET
90 #define FM_MODE
92 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
93 #define FM_PRESET
94 #define FM_MODE
95 /* This should be removeable if the whole tuning thing is sorted out since
96 proper tuning quiets the screen almost entirely in that extreme measures
97 have to be taken to hear any interference. */
98 #define HAVE_NOISY_IDLE_MODE
100 #elif CONFIG_KEYPAD == ONDIO_PAD
101 #define FM_RECORD_DBLPRE
102 #define FM_RECORD
103 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || (CONFIG_KEYPAD == SANSA_C200_PAD)
104 #define FM_MENU
105 #define FM_PRESET
106 #define FM_STOP
107 #define FM_MODE
108 #define FM_EXIT
109 #define FM_PLAY
111 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
112 #define FM_PRESET
113 #define FM_MODE
115 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
116 #define FM_MENU
117 #define FM_PRESET
118 #define FM_STOP
119 #define FM_MODE
120 #define FM_EXIT
121 #define FM_PLAY
123 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
124 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
125 #define FM_MENU
126 #define FM_STOP
127 #define FM_EXIT
128 #define FM_PLAY
129 #define FM_MODE
131 #endif
133 #define RADIO_SCAN_MODE 0
134 #define RADIO_PRESET_MODE 1
136 static int curr_preset = -1;
137 static int curr_freq; /* current frequency in Hz */
138 static int radio_mode = RADIO_SCAN_MODE;
139 static int search_dir = 0;
141 static int radio_status = FMRADIO_OFF;
142 static bool in_screen = false;
144 #define MAX_PRESETS 64
145 static bool presets_loaded = false, presets_changed = false;
146 static struct fmstation presets[MAX_PRESETS];
148 static char filepreset[MAX_PATH]; /* preset filename variable */
150 static int num_presets = 0; /* The number of presets in the preset list */
152 static void radio_save_presets(void);
153 static int handle_radio_presets(void);
154 static bool radio_menu(void);
155 static int radio_add_preset(void);
156 static int save_preset_list(void);
157 static int load_preset_list(void);
158 static int clear_preset_list(void);
160 static int scan_presets(void *viewports);
161 static void radio_off(void);
163 bool radio_scan_mode(void)
165 return radio_mode == RADIO_SCAN_MODE;
168 bool radio_is_stereo(void)
170 return tuner_get(RADIO_STEREO) && !global_settings.fm_force_mono;
172 int radio_current_frequency(void)
174 return curr_freq;
177 int radio_current_preset(void)
179 return curr_preset;
181 int radio_preset_count(void)
183 return num_presets;
185 const struct fmstation *radio_get_preset(int preset)
187 return &presets[preset];
189 /* Function to manipulate all yesno dialogues.
190 This function needs the output text as an argument. */
191 static bool yesno_pop(const char* text)
193 int i;
194 const char *lines[]={text};
195 const struct text_message message={lines, 1};
196 bool ret = (gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
197 FOR_NB_SCREENS(i)
198 screens[i].clear_viewport();
199 return ret;
202 void radio_init(void)
204 tuner_init();
205 radio_off();
208 int get_radio_status(void)
210 return radio_status;
213 bool in_radio_screen(void)
215 return in_screen;
218 /* TODO: Move some more of the control functionality to firmware
219 and clean up the mess */
221 /* secret flag for starting paused - prevents unmute */
222 #define FMRADIO_START_PAUSED 0x8000
223 void radio_start(void)
225 const struct fm_region_data *fmr;
226 bool start_paused;
228 if(radio_status == FMRADIO_PLAYING)
229 return;
231 fmr = &fm_region_data[global_settings.fm_region];
233 start_paused = radio_status & FMRADIO_START_PAUSED;
234 /* clear flag before any yielding */
235 radio_status &= ~FMRADIO_START_PAUSED;
237 if(radio_status == FMRADIO_OFF)
238 tuner_power(true);
240 curr_freq = global_status.last_frequency * fmr->freq_step + fmr->freq_min;
242 tuner_set(RADIO_SLEEP, 0); /* wake up the tuner */
244 if(radio_status == FMRADIO_OFF)
246 #ifdef HAVE_RADIO_REGION
247 tuner_set(RADIO_REGION, global_settings.fm_region);
248 #endif
249 tuner_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
252 tuner_set(RADIO_FREQUENCY, curr_freq);
254 #ifdef HAVE_RADIO_MUTE_TIMEOUT
256 unsigned long mute_timeout = current_tick + HZ;
257 if (radio_status != FMRADIO_OFF)
259 /* paused */
260 mute_timeout += HZ;
263 while(!tuner_get(RADIO_STEREO) && !tuner_get(RADIO_TUNED))
265 if(TIME_AFTER(current_tick, mute_timeout))
266 break;
267 yield();
270 #endif
272 /* keep radio from sounding initially */
273 if(!start_paused)
274 tuner_set(RADIO_MUTE, 0);
276 radio_status = FMRADIO_PLAYING;
277 } /* radio_start */
279 void radio_pause(void)
281 if(radio_status == FMRADIO_PAUSED)
282 return;
284 if(radio_status == FMRADIO_OFF)
286 radio_status |= FMRADIO_START_PAUSED;
287 radio_start();
290 tuner_set(RADIO_MUTE, 1);
291 tuner_set(RADIO_SLEEP, 1);
293 radio_status = FMRADIO_PAUSED;
294 } /* radio_pause */
296 static void radio_off(void)
298 tuner_set(RADIO_MUTE, 1);
299 tuner_set(RADIO_SLEEP, 1); /* low power mode, if available */
300 radio_status = FMRADIO_OFF;
301 tuner_power(false); /* status update, power off if avail. */
304 void radio_stop(void)
306 if(radio_status == FMRADIO_OFF)
307 return;
309 radio_off();
310 } /* radio_stop */
312 bool radio_hardware_present(void)
314 return tuner_get(RADIO_PRESENT);
317 /* Keep freq on the grid for the current region */
318 static int snap_freq_to_grid(int freq)
320 const struct fm_region_data * const fmr =
321 &fm_region_data[global_settings.fm_region];
323 /* Range clamp if out of range or just round to nearest */
324 if (freq < fmr->freq_min)
325 freq = fmr->freq_min;
326 else if (freq > fmr->freq_max)
327 freq = fmr->freq_max;
328 else
329 freq = (freq - fmr->freq_min + fmr->freq_step/2) /
330 fmr->freq_step * fmr->freq_step + fmr->freq_min;
332 return freq;
335 /* Find a matching preset to freq */
336 static int find_preset(int freq)
338 int i;
339 if(num_presets < 1)
340 return -1;
341 for(i = 0;i < MAX_PRESETS;i++)
343 if(freq == presets[i].frequency)
344 return i;
347 return -1;
350 /* Return the closest preset encountered in the search direction with
351 wraparound. */
352 static int find_closest_preset(int freq, int direction)
354 int i;
355 int lowpreset = 0;
356 int highpreset = 0;
357 int closest = -1;
359 if (direction == 0) /* direction == 0 isn't really used */
360 return 0;
362 for (i = 0; i < num_presets; i++)
364 int f = presets[i].frequency;
365 if (f == freq)
366 return i; /* Exact match = stop */
368 /* remember the highest and lowest presets for wraparound */
369 if (f < presets[lowpreset].frequency)
370 lowpreset = i;
371 if (f > presets[highpreset].frequency)
372 highpreset = i;
374 /* find the closest preset in the given direction */
375 if (direction > 0 && f > freq)
377 if (closest < 0 || f < presets[closest].frequency)
378 closest = i;
380 else if (direction < 0 && f < freq)
382 if (closest < 0 || f > presets[closest].frequency)
383 closest = i;
387 if (closest < 0)
389 /* no presets in the given direction */
390 /* wrap around depending on direction */
391 if (direction < 0)
392 closest = highpreset;
393 else
394 closest = lowpreset;
397 return closest;
400 static void remember_frequency(void)
402 const struct fm_region_data * const fmr =
403 &fm_region_data[global_settings.fm_region];
404 global_status.last_frequency = (curr_freq - fmr->freq_min)
405 / fmr->freq_step;
406 status_save();
409 static void next_preset(int direction)
411 if (num_presets < 1)
412 return;
414 if (curr_preset == -1)
415 curr_preset = find_closest_preset(curr_freq, direction);
416 else
417 curr_preset = (curr_preset + direction + num_presets) % num_presets;
419 /* Must stay on the current grid for the region */
420 curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
422 tuner_set(RADIO_FREQUENCY, curr_freq);
423 remember_frequency();
426 /* Step to the next or previous frequency */
427 static int step_freq(int freq, int direction)
429 const struct fm_region_data * const fmr =
430 &fm_region_data[global_settings.fm_region];
432 freq += direction*fmr->freq_step;
434 /* Wrap first or snapping to grid will not let us on the band extremes */
435 if (freq > fmr->freq_max)
436 freq = direction > 0 ? fmr->freq_min : fmr->freq_max;
437 else if (freq < fmr->freq_min)
438 freq = direction < 0 ? fmr->freq_max : fmr->freq_min;
439 else
440 freq = snap_freq_to_grid(freq);
442 return freq;
445 /* Step to the next or previous station */
446 static void next_station(int direction)
448 if (direction != 0 && radio_mode != RADIO_SCAN_MODE)
450 next_preset(direction);
451 return;
454 curr_freq = step_freq(curr_freq, direction);
456 if (radio_status == FMRADIO_PLAYING)
457 tuner_set(RADIO_MUTE, 1);
459 tuner_set(RADIO_FREQUENCY, curr_freq);
461 if (radio_status == FMRADIO_PLAYING)
462 tuner_set(RADIO_MUTE, 0);
464 curr_preset = find_preset(curr_freq);
465 remember_frequency();
468 /* Ends an in-progress search */
469 static void end_search(void)
471 if (search_dir != 0 && radio_status == FMRADIO_PLAYING)
472 tuner_set(RADIO_MUTE, 0);
473 search_dir = 0;
476 /* Speak a frequency. */
477 static void talk_freq(int freq, bool enqueue)
479 freq /= 10000;
480 talk_number(freq / 100, enqueue);
481 talk_id(LANG_POINT, true);
482 talk_number(freq % 100 / 10, true);
483 if (freq % 10)
484 talk_number(freq % 10, true);
487 /* Speak a preset by number or by spelling its name, depending on settings. */
488 static void talk_preset(int preset, bool fallback, bool enqueue)
490 if (global_settings.talk_file == 1) /* number */
491 talk_number(preset + 1, enqueue);
492 else
493 { /* spell */
494 if(presets[preset].name[0])
495 talk_spell(presets[preset].name, enqueue);
496 else if(fallback)
497 talk_freq(presets[preset].frequency, enqueue);
501 /* Skin stuff */
502 extern struct wps_state wps_state; /* from wps.c */
503 static struct gui_wps fms_skin[NB_SCREENS] = {{ .data = NULL }};
504 static struct wps_data fms_skin_data[NB_SCREENS] = {{ .wps_loaded = 0 }};
505 static struct wps_sync_data fms_skin_sync_data = { .do_full_update = false };
508 void fms_data_load(enum screen_type screen, const char *buf, bool isfile)
510 struct wps_data *data = fms_skin[screen].data;
511 int success;
512 success = buf && skin_data_load(screen, data, buf, isfile);
514 if (!success ) /* load the default */
516 const char default_fms[] = "%Sx|Station:| %tf\n"
517 "%?ts<%Sx|Stereo||%Sx|Mono|>\n"
518 "%?tm<%Sx|Mode:| %Sx|Scan||%Sx|Preset|: %Ti. %?Tn<%Tn|%Tf>>\n"
519 "%pb\n"
520 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
521 "%?Rr<%Sx|Time:| %Rh:%Rn:%Rs|"
522 "%?St|prerecording time|<%Sx|Prerecord Time| %Rs|%pm>>\n"
523 #endif
524 #ifdef HAVE_RDS_CAP
525 "\n%s%ty\n"
526 "%s%tz\n"
527 #endif
529 skin_data_load(screen, data, default_fms, false);
532 enum fms_exiting {
533 FMS_EXIT,
534 FMS_ENTER
536 void fms_fix_displays(enum fms_exiting toggle_state)
538 int i;
539 FOR_NB_SCREENS(i)
541 if (toggle_state == FMS_ENTER)
543 viewportmanager_theme_enable(i, skin_has_sbs(i, fms_skin[i].data), NULL);
544 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
545 screens[i].backdrop_show(fms_skin[i].data->backdrop);
546 #endif
547 screens[i].clear_display();
548 /* force statusbar/skin update since we just cleared the whole screen */
549 send_event(GUI_EVENT_ACTIONUPDATE, (void*)1);
551 else
553 screens[i].stop_scroll();
554 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
555 screens[i].backdrop_show(sb_get_backdrop(i));
556 #endif
557 viewportmanager_theme_undo(i, skin_has_sbs(i, fms_skin[i].data));
563 void fms_skin_init(void)
565 int i;
566 FOR_NB_SCREENS(i)
568 #ifdef HAVE_ALBUMART
569 fms_skin_data[i].albumart = NULL;
570 fms_skin_data[i].playback_aa_slot = -1;
571 #endif
572 fms_skin[i].data = &fms_skin_data[i];
573 fms_skin[i].display = &screens[i];
574 /* Currently no seperate wps_state needed/possible
575 so use the only available ( "global" ) one */
576 fms_skin[i].state = &wps_state;
577 fms_skin[i].sync_data = &fms_skin_sync_data;
581 int radio_screen(void)
583 bool done = false;
584 int ret_val = GO_TO_ROOT;
585 int button;
586 int i;
587 bool stereo = false, last_stereo = false;
588 bool update_screen = true, restore = true;
589 bool screen_freeze = false;
590 bool keep_playing = false;
591 bool talk = false;
592 #ifdef FM_RECORD_DBLPRE
593 int lastbutton = BUTTON_NONE;
594 unsigned long rec_lastclick = 0;
595 #endif
596 #if CONFIG_CODEC != SWCODEC
597 bool have_recorded = false;
598 int timeout = current_tick + HZ/10;
599 unsigned int last_seconds = 0;
600 #ifndef SIMULATOR
601 unsigned int seconds = 0;
602 struct audio_recording_options rec_options;
603 #endif
604 #endif /* CONFIG_CODEC != SWCODEC */
605 #ifndef HAVE_NOISY_IDLE_MODE
606 int button_timeout = current_tick + (2*HZ);
607 #endif
609 /* change status to "in screen" */
610 in_screen = true;
612 if(num_presets <= 0)
614 radio_load_presets(global_settings.fmr_file);
617 if(radio_status == FMRADIO_OFF)
618 audio_stop();
619 #ifndef SIMULATOR
621 #if CONFIG_CODEC != SWCODEC
622 if(rec_create_directory() > 0)
623 have_recorded = true;
625 audio_init_recording(talk_get_bufsize());
627 sound_settings_apply();
628 /* Yes, we use the D/A for monitoring */
629 peak_meter_playback(true);
631 peak_meter_enable(true);
633 rec_init_recording_options(&rec_options);
634 rec_options.rec_source = AUDIO_SRC_LINEIN;
635 rec_set_recording_options(&rec_options);
637 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
638 sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
640 #endif /* CONFIG_CODEC != SWCODEC */
641 #endif /* ndef SIMULATOR */
643 /* turn on radio */
644 #if CONFIG_CODEC == SWCODEC
645 audio_set_input_source(AUDIO_SRC_FMRADIO,
646 (radio_status == FMRADIO_PAUSED) ?
647 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
648 #else
649 if (radio_status == FMRADIO_OFF)
650 radio_start();
651 #endif
653 if(num_presets < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN)))
654 scan_presets(NULL);
656 curr_preset = find_preset(curr_freq);
657 if(curr_preset != -1)
658 radio_mode = RADIO_PRESET_MODE;
660 #ifndef HAVE_NOISY_IDLE_MODE
661 cpu_idle_mode(true);
662 #endif
664 while(!done)
666 if(search_dir != 0)
668 curr_freq = step_freq(curr_freq, search_dir);
669 update_screen = true;
671 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
673 curr_preset = find_preset(curr_freq);
674 remember_frequency();
675 end_search();
676 talk = true;
678 trigger_cpu_boost();
681 if (!update_screen)
683 cancel_cpu_boost();
686 button = skin_wait_for_action(fms_skin, CONTEXT_FM,
687 update_screen ? TIMEOUT_NOBLOCK : HZ);
689 #ifndef HAVE_NOISY_IDLE_MODE
690 if (button != ACTION_NONE)
692 cpu_idle_mode(false);
693 button_timeout = current_tick + (2*HZ);
695 #endif
696 switch(button)
698 case ACTION_FM_STOP:
699 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
700 if(audio_status() == AUDIO_STATUS_RECORD)
702 audio_stop();
704 else
705 #endif
707 done = true;
708 if(presets_changed)
710 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
712 if(filepreset[0] == '\0')
713 save_preset_list();
714 else
715 radio_save_presets();
718 /* Clear the preset list on exit. */
719 clear_preset_list();
721 update_screen = true;
722 break;
724 #ifdef FM_RECORD
725 case ACTION_FM_RECORD:
726 #ifdef FM_RECORD_DBLPRE
727 if (lastbutton != ACTION_FM_RECORD_DBLPRE)
729 rec_lastclick = 0;
730 break;
732 if (current_tick - rec_lastclick > HZ/2)
734 rec_lastclick = current_tick;
735 break;
737 #endif /* FM_RECORD_DBLPRE */
738 #ifndef SIMULATOR
739 if(audio_status() == AUDIO_STATUS_RECORD)
741 rec_command(RECORDING_CMD_START_NEWFILE);
742 update_screen = true;
744 else
746 have_recorded = true;
747 rec_command(RECORDING_CMD_START);
748 update_screen = true;
750 #endif /* SIMULATOR */
751 last_seconds = 0;
752 break;
753 #endif /* #ifdef FM_RECORD */
755 case ACTION_FM_EXIT:
756 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
757 if(audio_status() == AUDIO_STATUS_RECORD)
758 audio_stop();
759 #endif
760 keep_playing = true;
761 done = true;
762 ret_val = GO_TO_ROOT;
763 if(presets_changed)
765 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
767 if(filepreset[0] == '\0')
768 save_preset_list();
769 else
770 radio_save_presets();
774 /* Clear the preset list on exit. */
775 clear_preset_list();
777 break;
779 case ACTION_STD_PREV:
780 case ACTION_STD_NEXT:
781 next_station(button == ACTION_STD_PREV ? -1 : 1);
782 end_search();
783 update_screen = true;
784 talk = true;
785 break;
787 case ACTION_STD_PREVREPEAT:
788 case ACTION_STD_NEXTREPEAT:
790 int dir = search_dir;
791 search_dir = button == ACTION_STD_PREVREPEAT ? -1 : 1;
792 if (radio_mode != RADIO_SCAN_MODE)
794 next_preset(search_dir);
795 end_search();
796 update_screen = true;
797 talk = true;
799 else if (dir == 0)
801 /* Starting auto scan */
802 tuner_set(RADIO_MUTE, 1);
803 update_screen = true;
805 break;
808 case ACTION_SETTINGS_INC:
809 case ACTION_SETTINGS_INCREPEAT:
810 global_settings.volume++;
811 setvol();
812 update_screen = true;
813 break;
815 case ACTION_SETTINGS_DEC:
816 case ACTION_SETTINGS_DECREPEAT:
817 global_settings.volume--;
818 setvol();
819 update_screen = true;
820 break;
822 case ACTION_FM_PLAY:
823 if (radio_status == FMRADIO_PLAYING)
824 radio_pause();
825 else
826 radio_start();
828 update_screen = true;
829 talk = false;
830 talk_shutup();
831 break;
833 case ACTION_FM_MENU:
834 fms_fix_displays(FMS_EXIT);
835 radio_menu();
836 curr_preset = find_preset(curr_freq);
837 update_screen = true;
838 restore = true;
839 break;
841 #ifdef FM_PRESET
842 case ACTION_FM_PRESET:
843 if(num_presets < 1)
845 splash(HZ, ID2P(LANG_FM_NO_PRESETS));
846 update_screen = true;
847 break;
849 fms_fix_displays(FMS_EXIT);
850 handle_radio_presets();
851 update_screen = true;
852 restore = true;
853 break;
854 #endif /* FM_PRESET */
856 #ifdef FM_FREEZE
857 case ACTION_FM_FREEZE:
858 if(!screen_freeze)
860 splash(HZ, str(LANG_FM_FREEZE));
861 screen_freeze = true;
863 else
865 update_screen = true;
866 screen_freeze = false;
868 break;
869 #endif /* FM_FREEZE */
871 case SYS_USB_CONNECTED:
872 #if CONFIG_CODEC != SWCODEC
873 /* Only accept USB connection when not recording */
874 if(audio_status() != AUDIO_STATUS_RECORD)
875 #endif
877 default_event_handler(SYS_USB_CONNECTED);
878 screen_freeze = true; /* Cosmetic: makes sure the
879 radio screen doesn't redraw */
880 done = true;
882 break;
884 #ifdef FM_MODE
885 case ACTION_FM_MODE:
886 if(radio_mode == RADIO_SCAN_MODE)
888 /* Force scan mode if there are no presets. */
889 if(num_presets > 0)
890 radio_mode = RADIO_PRESET_MODE;
892 else
893 radio_mode = RADIO_SCAN_MODE;
894 update_screen = true;
895 cond_talk_ids_fq(radio_mode ?
896 LANG_PRESET : LANG_RADIO_SCAN_MODE);
897 talk = true;
898 break;
899 #endif /* FM_MODE */
901 #ifdef FM_NEXT_PRESET
902 case ACTION_FM_NEXT_PRESET:
903 next_preset(1);
904 end_search();
905 update_screen = true;
906 talk = true;
907 break;
908 #endif
910 #ifdef FM_PREV_PRESET
911 case ACTION_FM_PREV_PRESET:
912 next_preset(-1);
913 end_search();
914 update_screen = true;
915 talk = true;
916 break;
917 #endif
919 default:
920 default_event_handler(button);
921 #ifdef HAVE_RDS_CAP
922 if (tuner_get(RADIO_EVENT))
923 update_screen = true;
924 #endif
925 if (!tuner_get(RADIO_PRESENT))
927 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
928 if(audio_status() == AUDIO_STATUS_RECORD)
929 audio_stop();
930 #endif
931 keep_playing = false;
932 done = true;
933 ret_val = GO_TO_ROOT;
934 if(presets_changed)
936 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
938 if(filepreset[0] == '\0')
939 save_preset_list();
940 else
941 radio_save_presets();
945 /* Clear the preset list on exit. */
946 clear_preset_list();
948 break;
949 } /*switch(button)*/
951 #ifdef FM_RECORD_DBLPRE
952 if (button != ACTION_NONE)
953 lastbutton = button;
954 #endif
956 #if CONFIG_CODEC != SWCODEC
957 peak_meter_peek();
958 #endif
960 if(!screen_freeze)
962 /* Only display the peak meter when not recording */
963 #if CONFIG_CODEC != SWCODEC
964 if(TIME_AFTER(current_tick, timeout))
966 timeout = current_tick + HZ;
967 #else /* SWCODEC */
969 #endif /* CONFIG_CODEC == SWCODEC */
971 /* keep "mono" from always being displayed when paused */
972 if (radio_status != FMRADIO_PAUSED)
974 stereo = tuner_get(RADIO_STEREO) &&
975 !global_settings.fm_force_mono;
977 if(stereo != last_stereo)
979 update_screen = true;
980 last_stereo = stereo;
985 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
986 seconds = audio_recorded_time() / HZ;
987 if (update_screen || seconds > last_seconds || restore)
989 last_seconds = seconds;
990 #else
991 if (update_screen || restore)
993 #endif
994 if (restore)
995 fms_fix_displays(FMS_ENTER);
996 FOR_NB_SCREENS(i)
997 skin_update(&fms_skin[i], WPS_REFRESH_ALL);
998 restore = false;
1001 update_screen = false;
1003 if (global_settings.talk_file && talk
1004 && radio_status == FMRADIO_PAUSED)
1006 talk = false;
1007 bool enqueue = false;
1008 if (radio_mode == RADIO_SCAN_MODE)
1010 talk_freq(curr_freq, enqueue);
1011 enqueue = true;
1013 if (curr_preset >= 0)
1014 talk_preset(curr_preset, radio_mode == RADIO_PRESET_MODE,
1015 enqueue);
1018 #if CONFIG_CODEC != SWCODEC
1019 if(audio_status() & AUDIO_STATUS_ERROR)
1021 done = true;
1023 #endif
1025 #ifndef HAVE_NOISY_IDLE_MODE
1026 if (TIME_AFTER(current_tick, button_timeout))
1028 cpu_idle_mode(true);
1030 #endif
1031 } /*while(!done)*/
1033 #ifndef SIMULATOR
1034 #if CONFIG_CODEC != SWCODEC
1035 if(audio_status() & AUDIO_STATUS_ERROR)
1037 splash(0, str(LANG_DISK_FULL));
1038 audio_error_clear();
1040 while(1)
1042 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
1043 if(button == ACTION_FM_STOP)
1044 break;
1048 audio_init_playback();
1049 #endif /* CONFIG_CODEC != SWCODEC */
1051 sound_settings_apply();
1052 #endif /* SIMULATOR */
1054 if(keep_playing)
1056 /* Catch FMRADIO_PLAYING status for the sim. */
1057 #ifndef SIMULATOR
1058 #if CONFIG_CODEC != SWCODEC
1059 /* Enable the Left and right A/D Converter */
1060 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
1061 sound_default(SOUND_RIGHT_GAIN),
1062 AUDIO_GAIN_LINEIN);
1063 mas_codec_writereg(6, 0x4000);
1064 #endif
1065 end_search();
1066 #endif /* SIMULATOR */
1068 else
1070 #if CONFIG_CODEC == SWCODEC
1071 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
1072 #else
1073 radio_stop();
1074 #endif
1077 #ifndef HAVE_NOISY_IDLE_MODE
1078 cpu_idle_mode(false);
1079 #endif
1080 fms_fix_displays(FMS_EXIT);
1081 in_screen = false;
1082 #if CONFIG_CODEC != SWCODEC
1083 return have_recorded;
1084 #else
1085 return false;
1086 #endif
1087 } /* radio_screen */
1089 static void radio_save_presets(void)
1091 int fd;
1092 int i;
1094 fd = creat(filepreset, 0666);
1095 if(fd >= 0)
1097 for(i = 0;i < num_presets;i++)
1099 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
1101 close(fd);
1103 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
1104 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
1105 presets_changed = false;
1107 else
1109 splash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED));
1113 void radio_load_presets(char *filename)
1115 int fd;
1116 int rc;
1117 char buf[128];
1118 char *freq;
1119 char *name;
1120 bool done = false;
1121 int f;
1123 memset(presets, 0, sizeof(presets));
1124 num_presets = 0;
1126 /* No Preset in configuration. */
1127 if(filename[0] == '\0')
1129 filepreset[0] = '\0';
1130 return;
1132 /* Temporary preset, loaded until player shuts down. */
1133 else if(filename[0] == '/')
1134 strlcpy(filepreset, filename, sizeof(filepreset));
1135 /* Preset from default directory. */
1136 else
1137 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1138 FMPRESET_PATH, filename);
1140 fd = open_utf8(filepreset, O_RDONLY);
1141 if(fd >= 0)
1143 while(!done && num_presets < MAX_PRESETS)
1145 rc = read_line(fd, buf, 128);
1146 if(rc > 0)
1148 if(settings_parseline(buf, &freq, &name))
1150 f = atoi(freq);
1151 if(f) /* For backwards compatibility */
1153 struct fmstation * const fms = &presets[num_presets];
1154 fms->frequency = f;
1155 strlcpy(fms->name, name, MAX_FMPRESET_LEN+1);
1156 num_presets++;
1160 else
1161 done = true;
1163 close(fd);
1165 else /* invalid file name? */
1166 filepreset[0] = '\0';
1168 presets_loaded = num_presets > 0;
1169 presets_changed = false;
1173 static int radio_add_preset(void)
1175 char buf[MAX_FMPRESET_LEN + 1];
1177 if(num_presets < MAX_PRESETS)
1179 buf[0] = '\0';
1181 if (!kbd_input(buf, MAX_FMPRESET_LEN + 1))
1183 struct fmstation * const fms = &presets[num_presets];
1184 strcpy(fms->name, buf);
1185 fms->frequency = curr_freq;
1186 num_presets++;
1187 presets_changed = true;
1188 presets_loaded = num_presets > 0;
1189 return true;
1192 else
1194 splash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS));
1196 return false;
1199 /* needed to know which preset we are edit/delete-ing */
1200 static int selected_preset = -1;
1201 static int radio_edit_preset(void)
1203 char buf[MAX_FMPRESET_LEN + 1];
1205 if (num_presets > 0)
1207 struct fmstation * const fms = &presets[selected_preset];
1209 strcpy(buf, fms->name);
1211 if (!kbd_input(buf, MAX_FMPRESET_LEN + 1))
1213 strcpy(fms->name, buf);
1214 presets_changed = true;
1218 return 1;
1221 static int radio_delete_preset(void)
1223 if (num_presets > 0)
1225 struct fmstation * const fms = &presets[selected_preset];
1227 if (selected_preset >= --num_presets)
1228 selected_preset = num_presets - 1;
1230 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1231 (uintptr_t)fms);
1233 if (curr_preset >= num_presets)
1234 --curr_preset;
1237 /* Don't ask to save when all presets are deleted. */
1238 presets_changed = num_presets > 0;
1240 if (!presets_changed)
1242 /* The preset list will be cleared, switch to Scan Mode. */
1243 radio_mode = RADIO_SCAN_MODE;
1244 curr_preset = -1;
1245 presets_loaded = false;
1248 return 1;
1251 static int load_preset_list(void)
1253 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1256 static int save_preset_list(void)
1258 if(num_presets > 0)
1260 bool bad_file_name = true;
1262 if(!dir_exists(FMPRESET_PATH)) /* Check if there is preset folder */
1263 mkdir(FMPRESET_PATH);
1265 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1266 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1268 while(bad_file_name)
1270 if(!kbd_input(filepreset, sizeof(filepreset)))
1272 /* check the name: max MAX_FILENAME (20) chars */
1273 char* p2;
1274 char* p1;
1275 int len;
1276 p1 = strrchr(filepreset, '/');
1277 p2 = p1;
1278 while((p1) && (*p2) && (*p2 != '.'))
1279 p2++;
1280 len = (int)(p2-p1) - 1;
1281 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1283 /* no slash, too long or too short */
1284 splash(HZ, ID2P(LANG_INVALID_FILENAME));
1286 else
1288 /* add correct extension (easier to always write)
1289 at this point, p2 points to 0 or the extension dot */
1290 *p2 = '\0';
1291 strcat(filepreset,".fmr");
1292 bad_file_name = false;
1293 radio_save_presets();
1296 else
1298 /* user aborted */
1299 return false;
1303 else
1304 splash(HZ, ID2P(LANG_FM_NO_PRESETS));
1306 return true;
1309 static int clear_preset_list(void)
1311 /* Clear all the preset entries */
1312 memset(presets, 0, sizeof (presets));
1314 num_presets = 0;
1315 presets_loaded = false;
1316 /* The preset list will be cleared switch to Scan Mode. */
1317 radio_mode = RADIO_SCAN_MODE;
1318 curr_preset = -1;
1320 presets_changed = false; /* Don't ask to save when clearing the list. */
1322 return true;
1325 MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1326 ID2P(LANG_FM_EDIT_PRESET),
1327 radio_edit_preset, NULL, NULL, Icon_NOICON);
1328 MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1329 ID2P(LANG_FM_DELETE_PRESET),
1330 radio_delete_preset, NULL, NULL, Icon_NOICON);
1331 static int radio_preset_callback(int action,
1332 const struct menu_item_ex *this_item)
1334 if (action == ACTION_STD_OK)
1335 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1336 return action;
1337 (void)this_item;
1339 MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_PRESET),
1340 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1341 &radio_delete_preset_item);
1342 /* present a list of preset stations */
1343 static const char* presets_get_name(int selected_item, void *data,
1344 char *buffer, size_t buffer_len)
1346 (void)data;
1347 struct fmstation *p = &presets[selected_item];
1348 if(p->name[0])
1349 return p->name;
1350 int freq = p->frequency / 10000;
1351 int frac = freq % 100;
1352 freq /= 100;
1353 snprintf(buffer, buffer_len,
1354 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1355 return buffer;
1358 static int presets_speak_name(int selected_item, void * data)
1360 (void)data;
1361 talk_preset(selected_item, true, false);
1362 return 0;
1365 static int handle_radio_presets(void)
1367 struct gui_synclist lists;
1368 int result = 0;
1369 int action = ACTION_NONE;
1370 #ifdef HAVE_BUTTONBAR
1371 struct gui_buttonbar buttonbar;
1372 #endif
1374 if(presets_loaded == false)
1375 return result;
1377 #ifdef HAVE_BUTTONBAR
1378 gui_buttonbar_init(&buttonbar);
1379 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1380 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1381 str(LANG_FM_BUTTONBAR_EXIT),
1382 str(LANG_FM_BUTTONBAR_ACTION));
1383 gui_buttonbar_draw(&buttonbar);
1384 #endif
1385 gui_synclist_init(&lists, presets_get_name, NULL, false, 1, NULL);
1386 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
1387 gui_synclist_set_icon_callback(&lists, NULL);
1388 if(global_settings.talk_file)
1389 gui_synclist_set_voice_callback(&lists, presets_speak_name);
1390 gui_synclist_set_nb_items(&lists, num_presets);
1391 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1392 gui_synclist_speak_item(&lists);
1394 while (result == 0)
1396 gui_synclist_draw(&lists);
1397 list_do_action(CONTEXT_STD, TIMEOUT_BLOCK,
1398 &lists, &action, LIST_WRAP_UNLESS_HELD);
1399 switch (action)
1401 case ACTION_STD_MENU:
1402 if (radio_add_preset())
1404 gui_synclist_set_nb_items(&lists, num_presets);
1405 gui_synclist_select_item(&lists, num_presets - 1);
1407 break;
1408 case ACTION_STD_CANCEL:
1409 result = 1;
1410 break;
1411 case ACTION_STD_OK:
1412 curr_preset = gui_synclist_get_sel_pos(&lists);
1413 curr_freq = presets[curr_preset].frequency;
1414 next_station(0);
1415 remember_frequency();
1416 result = 1;
1417 break;
1418 case ACTION_F3:
1419 case ACTION_STD_CONTEXT:
1420 selected_preset = gui_synclist_get_sel_pos(&lists);
1421 do_menu(&handle_radio_preset_menu, NULL, NULL, false);
1422 gui_synclist_set_nb_items(&lists, num_presets);
1423 gui_synclist_select_item(&lists, selected_preset);
1424 gui_synclist_speak_item(&lists);
1425 break;
1426 default:
1427 if(default_event_handler(action) == SYS_USB_CONNECTED)
1428 result = 2;
1431 return result - 1;
1434 void toggle_mono_mode(bool mono)
1436 tuner_set(RADIO_FORCE_MONO, mono);
1439 void set_radio_region(int region)
1441 #ifdef HAVE_RADIO_REGION
1442 tuner_set(RADIO_REGION, region);
1443 #endif
1444 next_station(0);
1445 remember_frequency();
1446 (void)region;
1449 MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
1450 MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
1452 #ifndef FM_MODE
1453 static char* get_mode_text(int selected_item, void * data, char *buffer)
1455 (void)selected_item;
1456 (void)data;
1457 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_MODE),
1458 radio_mode ? str(LANG_PRESET) :
1459 str(LANG_RADIO_SCAN_MODE));
1460 return buffer;
1462 static int toggle_radio_mode(void)
1464 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
1465 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
1466 return 0;
1468 MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1469 toggle_radio_mode, NULL,
1470 get_mode_text, NULL, NULL, NULL, Icon_NOICON);
1471 #endif
1473 static int scan_presets(void *viewports)
1475 bool do_scan = true;
1476 int i;
1477 struct viewport *vp = (struct viewport *)viewports;
1479 FOR_NB_SCREENS(i)
1480 screens[i].set_viewport(vp?&vp[i]:NULL);
1481 if(num_presets > 0) /* Do that to avoid 2 questions. */
1482 do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS));
1484 if(do_scan)
1486 const struct fm_region_data * const fmr =
1487 &fm_region_data[global_settings.fm_region];
1489 curr_freq = fmr->freq_min;
1490 num_presets = 0;
1491 memset(presets, 0, sizeof(presets));
1493 tuner_set(RADIO_MUTE, 1);
1495 while(curr_freq <= fmr->freq_max)
1497 int freq, frac;
1498 if(num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1499 break;
1501 freq = curr_freq / 10000;
1502 frac = freq % 100;
1503 freq /= 100;
1505 splashf(0, str(LANG_FM_SCANNING), freq, frac);
1507 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
1509 /* add preset */
1510 presets[num_presets].name[0] = '\0';
1511 presets[num_presets].frequency = curr_freq;
1512 num_presets++;
1515 curr_freq += fmr->freq_step;
1518 if (radio_status == FMRADIO_PLAYING)
1519 tuner_set(RADIO_MUTE, 0);
1521 presets_changed = true;
1523 FOR_NB_SCREENS(i)
1525 screens[i].clear_viewport();
1526 screens[i].update_viewport();
1529 if(num_presets > 0)
1531 curr_freq = presets[0].frequency;
1532 radio_mode = RADIO_PRESET_MODE;
1533 presets_loaded = true;
1534 next_station(0);
1536 else
1538 /* Wrap it to beginning or we'll be past end of band */
1539 presets_loaded = false;
1540 next_station(1);
1543 return true;
1547 #ifdef HAVE_RECORDING
1549 #if defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC
1550 #define FM_RECORDING_SCREEN
1551 static int fm_recording_screen(void)
1553 bool ret;
1555 /* switch recording source to FMRADIO for the duration */
1556 int rec_source = global_settings.rec_source;
1557 global_settings.rec_source = AUDIO_SRC_FMRADIO;
1558 ret = recording_screen(true);
1560 /* safe to reset as changing sources is prohibited here */
1561 global_settings.rec_source = rec_source;
1563 return ret;
1566 #endif /* defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC */
1568 #if defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC
1569 #define FM_RECORDING_SETTINGS
1570 static int fm_recording_settings(void)
1572 bool ret = recording_menu(true);
1574 #if CONFIG_CODEC != SWCODEC
1575 if (!ret)
1577 struct audio_recording_options rec_options;
1578 rec_init_recording_options(&rec_options);
1579 rec_options.rec_source = AUDIO_SRC_LINEIN;
1580 rec_set_recording_options(&rec_options);
1582 #endif
1584 return ret;
1587 #endif /* defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC */
1588 #endif /* HAVE_RECORDING */
1590 #ifdef FM_RECORDING_SCREEN
1591 MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING),
1592 fm_recording_screen, NULL, NULL, Icon_Recording);
1593 #endif
1594 #ifdef FM_RECORDING_SETTINGS
1595 MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1596 fm_recording_settings, NULL, NULL, Icon_Recording);
1597 #endif
1598 #ifndef FM_PRESET
1599 MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
1600 handle_radio_presets, NULL, NULL, Icon_NOICON);
1601 #endif
1602 #ifndef FM_PRESET_ADD
1603 MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1604 radio_add_preset, NULL, NULL, Icon_NOICON);
1605 #endif
1608 MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1609 load_preset_list, NULL, NULL, Icon_NOICON);
1610 MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1611 save_preset_list, NULL, NULL, Icon_NOICON);
1612 MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1613 clear_preset_list, NULL, NULL, Icon_NOICON);
1614 MENUITEM_FUNCTION(scan_presets_item, MENU_FUNC_USEPARAM,
1615 ID2P(LANG_FM_SCAN_PRESETS),
1616 scan_presets, NULL, NULL, Icon_NOICON);
1618 MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
1619 Icon_Radio_screen,
1620 #ifndef FM_PRESET
1621 &radio_presets_item,
1622 #endif
1623 #ifndef FM_PRESET_ADD
1624 &radio_addpreset_item,
1625 #endif
1626 &presetload_item, &presetsave_item, &presetclear_item,
1627 &force_mono,
1628 #ifndef FM_MODE
1629 &radio_mode_item,
1630 #endif
1631 &set_region, &sound_settings,
1632 #ifdef FM_RECORDING_SCREEN
1633 &recscreen_item,
1634 #endif
1635 #ifdef FM_RECORDING_SETTINGS
1636 &recsettings_item,
1637 #endif
1638 &scan_presets_item);
1639 /* main menu of the radio screen */
1640 static bool radio_menu(void)
1642 return do_menu(&radio_settings_menu, NULL, NULL, false) ==
1643 MENU_ATTACHED_USB;
1646 #endif