Redo my previous segfault fix in a better way.
[Rockbox.git] / apps / recorder / radio.c
blob0e43cc67359d1164838703a247810ff709e455e8
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2003 Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "config.h"
21 #include <stdio.h>
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include "sprintf.h"
25 #include "mas.h"
26 #include "settings.h"
27 #include "button.h"
28 #include "status.h"
29 #include "thread.h"
30 #include "mpeg.h"
31 #include "audio.h"
32 #include "mp3_playback.h"
33 #include "ctype.h"
34 #include "file.h"
35 #include "errno.h"
36 #include "atoi.h"
37 #include "string.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 #include "talk.h"
52 #include "tuner.h"
53 #include "power.h"
54 #include "sound.h"
55 #include "screen_access.h"
56 #include "statusbar.h"
57 #include "textarea.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"
69 #if CONFIG_TUNER
71 #if CONFIG_KEYPAD == RECORDER_PAD
72 #define FM_RECORD
73 #define FM_PRESET_ADD
74 #define FM_PRESET_ACTION
75 #define FM_PRESET
76 #define FM_MODE
78 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
79 #define FM_PRESET
80 #define FM_MODE
81 #define FM_NEXT_PRESET
82 #define FM_PREV_PRESET
84 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
85 #define FM_PRESET
86 #define FM_MODE
88 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
89 #define FM_PRESET
90 #define FM_MODE
91 /* This should be removeable if the whole tuning thing is sorted out since
92 proper tuning quiets the screen almost entirely in that extreme measures
93 have to be taken to hear any interference. */
94 #define HAVE_NOISY_IDLE_MODE
96 #elif CONFIG_KEYPAD == ONDIO_PAD
97 #define FM_RECORD_DBLPRE
98 #define FM_RECORD
99 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || (CONFIG_KEYPAD == SANSA_C200_PAD)
100 #define FM_MENU
101 #define FM_PRESET
102 #define FM_STOP
103 #define FM_MODE
104 #define FM_EXIT
105 #define FM_PLAY
106 #endif
108 #define RADIO_SCAN_MODE 0
109 #define RADIO_PRESET_MODE 1
111 static int curr_preset = -1;
112 static int curr_freq;
113 static int radio_mode = RADIO_SCAN_MODE;
114 static int search_dir = 0;
116 static int radio_status = FMRADIO_OFF;
117 static bool in_screen = false;
119 #define MAX_PRESETS 64
120 static bool presets_loaded = false, presets_changed = false;
121 static struct fmstation presets[MAX_PRESETS];
123 static char filepreset[MAX_PATH]; /* preset filename variable */
125 static int num_presets = 0; /* The number of presets in the preset list */
127 static void radio_save_presets(void);
128 static int handle_radio_presets(void);
129 static bool radio_menu(void);
130 static int radio_add_preset(void);
131 static int save_preset_list(void);
132 static int load_preset_list(void);
133 static int clear_preset_list(void);
135 static int scan_presets(void);
137 /* Function to manipulate all yesno dialogues.
138 This function needs the output text as an argument. */
139 static bool yesno_pop(char* text)
141 int i;
142 char *lines[]={text};
143 struct text_message message={lines, 1};
144 bool ret = (gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
145 FOR_NB_SCREENS(i)
146 gui_textarea_clear(&screens[i]);
147 return ret;
150 void radio_init(void)
152 tuner_init();
153 radio_stop();
156 int get_radio_status(void)
158 return radio_status;
161 bool in_radio_screen(void)
163 return in_screen;
166 /* TODO: Move some more of the control functionality to an HAL and clean up the
167 mess */
169 /* secret flag for starting paused - prevents unmute */
170 #define FMRADIO_START_PAUSED 0x8000
171 void radio_start(void)
173 const struct fm_region_data *fmr;
174 bool start_paused;
176 if(radio_status == FMRADIO_PLAYING)
177 return;
179 fmr = &fm_region_data[global_settings.fm_region];
181 start_paused = radio_status & FMRADIO_START_PAUSED;
182 /* clear flag before any yielding */
183 radio_status &= ~FMRADIO_START_PAUSED;
185 if(radio_status == FMRADIO_OFF)
186 tuner_power(true);
188 curr_freq = global_status.last_frequency
189 * 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 #ifdef HAS_BUTTONBAR
456 struct gui_buttonbar buttonbar;
457 gui_buttonbar_init(&buttonbar);
458 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
459 #endif
461 /* change status to "in screen" */
462 in_screen = true;
464 /* always display status bar in radio screen for now */
465 global_status.statusbar_forced = statusbar?0:1;
466 global_settings.statusbar = true;
467 FOR_NB_SCREENS(i)
469 gui_textarea_clear(&screens[i]);
470 screen_set_xmargin(&screens[i],0);
473 gui_syncstatusbar_draw(&statusbars,true);
475 fh = font_get(FONT_UI)->height;
477 /* Adjust for font size, trying to center the information vertically */
478 if(fh < 10)
479 top_of_screen = 1;
481 if(num_presets <= 0)
483 memset(presets, 0, sizeof(presets));
484 radio_load_presets(global_settings.fmr_file);
487 if(radio_status == FMRADIO_OFF)
488 audio_stop();
489 #ifndef SIMULATOR
491 #if CONFIG_CODEC != SWCODEC
492 if(rec_create_directory() > 0)
493 have_recorded = true;
495 audio_init_recording(talk_get_bufsize());
497 sound_settings_apply();
498 /* Yes, we use the D/A for monitoring */
499 peak_meter_playback(true);
501 peak_meter_enabled = true;
503 rec_init_recording_options(&rec_options);
504 rec_options.rec_source = AUDIO_SRC_LINEIN;
505 rec_set_recording_options(&rec_options);
507 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
508 sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
510 #endif /* CONFIG_CODEC != SWCODEC */
511 #endif /* ndef SIMULATOR */
513 /* turn on radio */
514 #if CONFIG_CODEC == SWCODEC
515 audio_set_input_source(AUDIO_SRC_FMRADIO,
516 (radio_status == FMRADIO_PAUSED) ?
517 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
518 #else
519 if (radio_status == FMRADIO_OFF)
520 radio_start();
521 #endif
523 if(num_presets < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN)))
524 scan_presets();
526 curr_preset = find_preset(curr_freq);
527 if(curr_preset != -1)
528 radio_mode = RADIO_PRESET_MODE;
530 #ifdef HAS_BUTTONBAR
531 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
532 str(LANG_PRESET), str(LANG_FM_BUTTONBAR_RECORD));
533 #endif
535 #ifndef HAVE_NOISY_IDLE_MODE
536 cpu_idle_mode(true);
537 #endif
539 while(!done)
541 if(search_dir != 0)
543 curr_freq = step_freq(curr_freq, search_dir);
544 update_screen = true;
546 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
548 curr_preset = find_preset(curr_freq);
549 remember_frequency();
550 end_search();
551 talk = true;
554 trigger_cpu_boost();
557 if (!update_screen)
559 cancel_cpu_boost();
562 #if CONFIG_CODEC != SWCODEC
563 /* TODO: Can we timeout at HZ when recording since peaks aren't
564 displayed? This should quiet recordings too. */
565 button = get_action(CONTEXT_FM,
566 update_screen ? TIMEOUT_NOBLOCK : HZ / PEAK_METER_FPS);
567 #else
568 button = get_action(CONTEXT_FM,
569 update_screen ? TIMEOUT_NOBLOCK : HZ);
570 #endif
572 #ifndef HAVE_NOISY_IDLE_MODE
573 if (button != ACTION_NONE)
575 cpu_idle_mode(false);
576 button_timeout = current_tick + (2*HZ);
578 #endif
579 switch(button)
581 case ACTION_FM_STOP:
582 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
583 if(audio_status() == AUDIO_STATUS_RECORD)
585 audio_stop();
587 else
588 #endif
590 done = true;
591 if(presets_changed)
593 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
595 if(filepreset[0] == '\0')
596 save_preset_list();
597 else
598 radio_save_presets();
601 /* Clear the preset list on exit. */
602 clear_preset_list();
604 update_screen = true;
605 break;
607 #ifdef FM_RECORD
608 case ACTION_FM_RECORD:
609 #ifdef FM_RECORD_DBLPRE
610 if (lastbutton != ACTION_FM_RECORD_DBLPRE)
612 rec_lastclick = 0;
613 break;
615 if (current_tick - rec_lastclick > HZ/2)
617 rec_lastclick = current_tick;
618 break;
620 #endif /* FM_RECORD_DBLPRE */
621 #ifndef SIMULATOR
622 if(audio_status() == AUDIO_STATUS_RECORD)
624 rec_command(RECORDING_CMD_START_NEWFILE);
625 update_screen = true;
627 else
629 have_recorded = true;
630 rec_command(RECORDING_CMD_START);
631 update_screen = true;
633 #endif /* SIMULATOR */
634 last_seconds = 0;
635 break;
636 #endif /* #ifdef FM_RECORD */
638 case ACTION_FM_EXIT:
639 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
640 if(audio_status() == AUDIO_STATUS_RECORD)
641 audio_stop();
642 #endif
643 keep_playing = true;
644 done = true;
645 ret_val = GO_TO_ROOT;
646 if(presets_changed)
648 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
650 if(filepreset[0] == '\0')
651 save_preset_list();
652 else
653 radio_save_presets();
657 /* Clear the preset list on exit. */
658 clear_preset_list();
660 break;
662 case ACTION_STD_PREV:
663 case ACTION_STD_NEXT:
664 next_station(button == ACTION_STD_PREV ? -1 : 1);
665 end_search();
666 update_screen = true;
667 talk = true;
668 break;
670 case ACTION_STD_PREVREPEAT:
671 case ACTION_STD_NEXTREPEAT:
673 int dir = search_dir;
674 search_dir = button == ACTION_STD_PREVREPEAT ? -1 : 1;
675 if (radio_mode != RADIO_SCAN_MODE)
677 next_preset(search_dir);
678 end_search();
679 update_screen = true;
680 talk = true;
682 else if (dir == 0)
684 /* Starting auto scan */
685 tuner_set(RADIO_MUTE, 1);
686 update_screen = true;
688 break;
691 case ACTION_SETTINGS_INC:
692 case ACTION_SETTINGS_INCREPEAT:
693 global_settings.volume++;
694 setvol();
695 update_screen = true;
696 break;
698 case ACTION_SETTINGS_DEC:
699 case ACTION_SETTINGS_DECREPEAT:
700 global_settings.volume--;
701 setvol();
702 update_screen = true;
703 break;
705 case ACTION_FM_PLAY:
706 if (radio_status == FMRADIO_PLAYING)
707 radio_pause();
708 else
709 radio_start();
711 update_screen = true;
712 talk = false;
713 talk_shutup();
714 break;
716 case ACTION_FM_MENU:
717 radio_menu();
718 curr_preset = find_preset(curr_freq);
719 FOR_NB_SCREENS(i){
720 struct screen *sc = &screens[i];
721 gui_textarea_clear(sc);
722 screen_set_xmargin(sc, 0);
724 #ifdef HAS_BUTTONBAR
725 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
726 str(LANG_PRESET),
727 str(LANG_FM_BUTTONBAR_RECORD));
728 #endif
729 update_screen = true;
730 break;
732 #ifdef FM_PRESET
733 case ACTION_FM_PRESET:
734 if(num_presets < 1)
736 gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS));
737 update_screen = true;
738 FOR_NB_SCREENS(i)
740 struct screen *sc = &screens[i];
741 gui_textarea_clear(sc);
742 screen_set_xmargin(sc, 0);
743 gui_textarea_update(sc);
746 break;
748 handle_radio_presets();
749 FOR_NB_SCREENS(i)
751 struct screen *sc = &screens[i];
752 gui_textarea_clear(sc);
753 screen_set_xmargin(sc, 0);
754 gui_textarea_update(sc);
756 #ifdef HAS_BUTTONBAR
757 gui_buttonbar_set(&buttonbar,
758 str(LANG_BUTTONBAR_MENU),
759 str(LANG_PRESET),
760 str(LANG_FM_BUTTONBAR_RECORD));
761 #endif
762 update_screen = true;
763 break;
764 #endif /* FM_PRESET */
766 #ifdef FM_FREEZE
767 case ACTION_FM_FREEZE:
768 if(!screen_freeze)
770 gui_syncsplash(HZ, str(LANG_FM_FREEZE));
771 screen_freeze = true;
773 else
775 update_screen = true;
776 screen_freeze = false;
778 break;
779 #endif /* FM_FREEZE */
781 case SYS_USB_CONNECTED:
782 #if CONFIG_CODEC != SWCODEC
783 /* Only accept USB connection when not recording */
784 if(audio_status() != AUDIO_STATUS_RECORD)
785 #endif
787 default_event_handler(SYS_USB_CONNECTED);
788 screen_freeze = true; /* Cosmetic: makes sure the
789 radio screen doesn't redraw */
790 done = true;
792 break;
794 #ifdef FM_MODE
795 case ACTION_FM_MODE:
796 if(radio_mode == RADIO_SCAN_MODE)
798 /* Force scan mode if there are no presets. */
799 if(num_presets > 0)
800 radio_mode = RADIO_PRESET_MODE;
802 else
803 radio_mode = RADIO_SCAN_MODE;
804 update_screen = true;
805 cond_talk_ids_fq(radio_mode ?
806 LANG_PRESET : LANG_RADIO_SCAN_MODE);
807 talk = true;
808 break;
809 #endif /* FM_MODE */
811 #ifdef FM_NEXT_PRESET
812 case ACTION_FM_NEXT_PRESET:
813 next_preset(1);
814 end_search();
815 update_screen = true;
816 talk = true;
817 break;
818 #endif
820 #ifdef FM_PREV_PRESET
821 case ACTION_FM_PREV_PRESET:
822 next_preset(-1);
823 end_search();
824 update_screen = true;
825 talk = true;
826 break;
827 #endif
829 default:
830 default_event_handler(button);
831 break;
832 } /*switch(button)*/
834 #ifdef FM_RECORD_DBLPRE
835 if (button != ACTION_NONE)
836 lastbutton = button;
837 #endif
839 #if CONFIG_CODEC != SWCODEC
840 peak_meter_peek();
841 #endif
843 if(!screen_freeze)
845 /* Only display the peak meter when not recording */
846 #if CONFIG_CODEC != SWCODEC
847 if(!audio_status())
849 FOR_NB_SCREENS(i)
851 peak_meter_screen(&screens[i],0,
852 STATUSBAR_HEIGHT + fh*(top_of_screen + 4), fh);
853 screens[i].update_rect(0, STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
854 screens[i].width, fh);
858 if(TIME_AFTER(current_tick, timeout))
860 timeout = current_tick + HZ;
861 #else /* SWCODEC */
863 #endif /* CONFIG_CODEC == SWCODEC */
865 /* keep "mono" from always being displayed when paused */
866 if (radio_status != FMRADIO_PAUSED)
868 stereo = tuner_get(RADIO_STEREO) &&
869 !global_settings.fm_force_mono;
871 if(stereo != last_stereo)
873 update_screen = true;
874 last_stereo = stereo;
879 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
880 seconds = audio_recorded_time() / HZ;
881 if (update_screen || seconds > last_seconds)
883 last_seconds = seconds;
884 #else
885 if (update_screen)
887 #endif
888 int freq;
890 FOR_NB_SCREENS(i)
891 screens[i].setfont(FONT_UI);
893 snprintf(buf, 128, curr_preset >= 0 ? "%d. %s" : " ",
894 curr_preset + 1, presets[curr_preset].name);
896 FOR_NB_SCREENS(i)
897 screens[i].puts_scroll(0, top_of_screen, buf);
899 freq = curr_freq / 10000;
900 snprintf(buf, 128, str(LANG_FM_STATION), freq / 100, freq % 100);
901 FOR_NB_SCREENS(i)
902 screens[i].puts_scroll(0, top_of_screen + 1, buf);
904 snprintf(buf, 128, stereo?str(LANG_CHANNEL_STEREO):
905 str(LANG_CHANNEL_MONO));
906 FOR_NB_SCREENS(i)
907 screens[i].puts_scroll(0, top_of_screen + 2, buf);
909 snprintf(buf, 128, "%s %s", str(LANG_MODE),
910 radio_mode ? str(LANG_PRESET) :
911 str(LANG_RADIO_SCAN_MODE));
912 FOR_NB_SCREENS(i)
913 screens[i].puts_scroll(0, top_of_screen + 3, buf);
915 #if CONFIG_CODEC != SWCODEC
916 if(audio_status() == AUDIO_STATUS_RECORD)
918 hours = seconds / 3600;
919 minutes = (seconds - (hours * 3600)) / 60;
920 snprintf(buf, 32, "%s %02d:%02d:%02d",
921 str(LANG_RECORDING_TIME),
922 hours, minutes, seconds%60);
923 FOR_NB_SCREENS(i)
924 screens[i].puts_scroll(0, top_of_screen + 4, buf);
926 else
928 if(rec_options.rec_prerecord_time)
930 snprintf(buf, 32, "%s %02d",
931 str(LANG_RECORD_PRERECORD), seconds%60);
932 FOR_NB_SCREENS(i)
933 screens[i].puts_scroll(0, top_of_screen + 4, buf);
936 #endif /* CONFIG_CODEC != SWCODEC */
938 #ifdef HAS_BUTTONBAR
939 gui_buttonbar_draw(&buttonbar);
940 #endif
941 FOR_NB_SCREENS(i)
942 gui_textarea_update(&screens[i]);
944 /* Only force the redraw if update_screen is true */
945 gui_syncstatusbar_draw(&statusbars,true);
948 update_screen = false;
950 if (global_settings.talk_file && talk
951 && radio_status == FMRADIO_PAUSED)
953 talk = false;
954 bool enqueue = false;
955 if (radio_mode == RADIO_SCAN_MODE)
957 talk_freq(curr_freq, enqueue);
958 enqueue = true;
960 if (curr_preset >= 0)
961 talk_preset(curr_preset, radio_mode == RADIO_PRESET_MODE,
962 enqueue);
965 #if CONFIG_CODEC != SWCODEC
966 if(audio_status() & AUDIO_STATUS_ERROR)
968 done = true;
970 #endif
972 #ifndef HAVE_NOISY_IDLE_MODE
973 if (TIME_AFTER(current_tick, button_timeout))
975 cpu_idle_mode(true);
977 #endif
978 } /*while(!done)*/
980 #ifndef SIMULATOR
981 #if CONFIG_CODEC != SWCODEC
982 if(audio_status() & AUDIO_STATUS_ERROR)
984 gui_syncsplash(0, str(LANG_DISK_FULL));
985 gui_syncstatusbar_draw(&statusbars,true);
986 FOR_NB_SCREENS(i)
987 gui_textarea_update(&screens[i]);
988 audio_error_clear();
990 while(1)
992 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
993 if(button == ACTION_FM_STOP)
994 break;
998 audio_init_playback();
999 #endif /* CONFIG_CODEC != SWCODEC */
1001 sound_settings_apply();
1002 #endif /* SIMULATOR */
1004 if(keep_playing)
1006 /* Catch FMRADIO_PLAYING status for the sim. */
1007 #ifndef SIMULATOR
1008 #if CONFIG_CODEC != SWCODEC
1009 /* Enable the Left and right A/D Converter */
1010 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
1011 sound_default(SOUND_RIGHT_GAIN),
1012 AUDIO_GAIN_LINEIN);
1013 mas_codec_writereg(6, 0x4000);
1014 #endif
1015 end_search();
1016 #endif /* SIMULATOR */
1018 else
1020 #if CONFIG_CODEC == SWCODEC
1021 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
1022 #else
1023 radio_stop();
1024 #endif
1027 #ifndef HAVE_NOISY_IDLE_MODE
1028 cpu_idle_mode(false);
1029 #endif
1031 /* restore status bar settings */
1032 global_settings.statusbar = statusbar;
1033 global_status.statusbar_forced = 0;
1034 in_screen = false;
1035 #if CONFIG_CODEC != SWCODEC
1036 return have_recorded;
1037 #else
1038 return false;
1039 #endif
1040 } /* radio_screen */
1042 static void radio_save_presets(void)
1044 int fd;
1045 int i;
1047 fd = creat(filepreset);
1048 if(fd >= 0)
1050 for(i = 0;i < num_presets;i++)
1052 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
1054 close(fd);
1056 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
1057 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
1058 presets_changed = false;
1060 else
1062 gui_syncsplash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED));
1066 void radio_load_presets(char *filename)
1068 int fd;
1069 int rc;
1070 char buf[128];
1071 char *freq;
1072 char *name;
1073 bool done = false;
1074 int f;
1076 memset(presets, 0, sizeof(presets));
1077 num_presets = 0;
1079 /* No Preset in configuration. */
1080 if(filename[0] == '\0')
1082 filepreset[0] = '\0';
1083 return;
1085 /* Temporary preset, loaded until player shuts down. */
1086 else if(filename[0] == '/')
1087 strncpy(filepreset, filename, sizeof(filepreset));
1088 /* Preset from default directory. */
1089 else
1090 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1091 FMPRESET_PATH, filename);
1093 fd = open(filepreset, O_RDONLY);
1094 if(fd >= 0)
1096 while(!done && num_presets < MAX_PRESETS)
1098 rc = read_line(fd, buf, 128);
1099 if(rc > 0)
1101 if(settings_parseline(buf, &freq, &name))
1103 f = atoi(freq);
1104 if(f) /* For backwards compatibility */
1106 struct fmstation * const fms = &presets[num_presets];
1107 fms->frequency = f;
1108 strncpy(fms->name, name, MAX_FMPRESET_LEN);
1109 fms->name[MAX_FMPRESET_LEN] = '\0';
1110 num_presets++;
1114 else
1115 done = true;
1117 close(fd);
1119 else /* invalid file name? */
1120 filepreset[0] = '\0';
1122 presets_loaded = num_presets > 0;
1123 presets_changed = false;
1127 static int radio_add_preset(void)
1129 char buf[MAX_FMPRESET_LEN + 1];
1131 if(num_presets < MAX_PRESETS)
1133 memset(buf, 0, MAX_FMPRESET_LEN);
1135 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1137 struct fmstation * const fms = &presets[num_presets];
1138 buf[MAX_FMPRESET_LEN] = '\0';
1139 strcpy(fms->name, buf);
1140 fms->frequency = curr_freq;
1141 num_presets++;
1142 presets_changed = true;
1143 presets_loaded = num_presets > 0;
1146 else
1148 gui_syncsplash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS));
1150 return true;
1153 /* needed to know which preset we are edit/delete-ing */
1154 static int selected_preset = -1;
1155 static int radio_edit_preset(void)
1157 char buf[MAX_FMPRESET_LEN + 1];
1159 if (num_presets > 0)
1161 struct fmstation * const fms = &presets[selected_preset];
1163 strncpy(buf, fms->name, MAX_FMPRESET_LEN);
1165 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1167 buf[MAX_FMPRESET_LEN] = '\0';
1168 strcpy(fms->name, buf);
1169 presets_changed = true;
1173 return 1;
1176 static int radio_delete_preset(void)
1178 if (num_presets > 0)
1180 struct fmstation * const fms = &presets[selected_preset];
1182 if (selected_preset >= --num_presets)
1183 selected_preset = num_presets - 1;
1185 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1186 (uintptr_t)fms);
1190 /* Don't ask to save when all presets are deleted. */
1191 presets_changed = num_presets > 0;
1193 if (!presets_changed)
1195 /* The preset list will be cleared, switch to Scan Mode. */
1196 radio_mode = RADIO_SCAN_MODE;
1197 presets_loaded = false;
1200 return 1;
1203 static int load_preset_list(void)
1205 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1208 static int save_preset_list(void)
1210 if(num_presets > 0)
1212 bool bad_file_name = true;
1214 if(!opendir(FMPRESET_PATH)) /* Check if there is preset folder */
1215 mkdir(FMPRESET_PATH);
1217 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1218 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1220 while(bad_file_name)
1222 if(!kbd_input(filepreset, sizeof(filepreset)))
1224 /* check the name: max MAX_FILENAME (20) chars */
1225 char* p2;
1226 char* p1;
1227 int len;
1228 p1 = strrchr(filepreset, '/');
1229 p2 = p1;
1230 while((p1) && (*p2) && (*p2 != '.'))
1231 p2++;
1232 len = (int)(p2-p1) - 1;
1233 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1235 /* no slash, too long or too short */
1236 gui_syncsplash(HZ, ID2P(LANG_INVALID_FILENAME));
1238 else
1240 /* add correct extension (easier to always write)
1241 at this point, p2 points to 0 or the extension dot */
1242 *p2 = '\0';
1243 strcat(filepreset,".fmr");
1244 bad_file_name = false;
1245 radio_save_presets();
1248 else
1250 /* user aborted */
1251 return false;
1255 else
1256 gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS));
1258 return true;
1261 static int clear_preset_list(void)
1263 /* Clear all the preset entries */
1264 memset(presets, 0, sizeof (presets));
1266 num_presets = 0;
1267 presets_loaded = false;
1268 /* The preset list will be cleared switch to Scan Mode. */
1269 radio_mode = RADIO_SCAN_MODE;
1271 presets_changed = false; /* Don't ask to save when clearing the list. */
1273 return true;
1276 MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1277 ID2P(LANG_FM_EDIT_PRESET),
1278 radio_edit_preset, NULL, NULL, Icon_NOICON);
1279 MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1280 ID2P(LANG_FM_DELETE_PRESET),
1281 radio_delete_preset, NULL, NULL, Icon_NOICON);
1282 int radio_preset_callback(int action, const struct menu_item_ex *this_item)
1284 if (action == ACTION_STD_OK)
1285 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1286 return action;
1287 (void)this_item;
1289 MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_PRESET),
1290 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1291 &radio_delete_preset_item);
1292 /* present a list of preset stations */
1293 char * presets_get_name(int selected_item, void * data, char *buffer)
1295 (void)data;
1296 struct fmstation *p = &presets[selected_item];
1297 if(p->name[0])
1298 return p->name;
1299 int freq = p->frequency / 10000;
1300 int frac = freq % 100;
1301 freq /= 100;
1302 snprintf(buffer, MAX_PATH,
1303 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1304 return buffer;
1307 static int presets_speak_name(int selected_item, void * data)
1309 (void)data;
1310 talk_preset(selected_item, true, false);
1311 return 0;
1314 static int handle_radio_presets(void)
1316 struct gui_synclist lists;
1317 int result = 0;
1318 int action = ACTION_NONE;
1319 #ifdef HAS_BUTTONBAR
1320 struct gui_buttonbar buttonbar;
1321 #endif
1323 if(presets_loaded == false)
1324 return result;
1326 #ifdef HAS_BUTTONBAR
1327 gui_buttonbar_init(&buttonbar);
1328 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1329 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1330 str(LANG_FM_BUTTONBAR_EXIT),
1331 str(LANG_FM_BUTTONBAR_ACTION));
1332 gui_buttonbar_draw(&buttonbar);
1333 #endif
1334 gui_synclist_init(&lists, presets_get_name, NULL, false, 1);
1335 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
1336 gui_synclist_set_icon_callback(&lists, NULL);
1337 if(global_settings.talk_file)
1338 gui_synclist_set_voice_callback(&lists, presets_speak_name);
1339 gui_synclist_set_nb_items(&lists, num_presets);
1340 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1341 gui_synclist_speak_item(&lists);
1343 while (result == 0)
1345 gui_synclist_draw(&lists);
1346 gui_syncstatusbar_draw(&statusbars, true);
1347 list_do_action(CONTEXT_STD, HZ,
1348 &lists, &action, LIST_WRAP_UNLESS_HELD);
1349 switch (action)
1351 case ACTION_STD_MENU:
1352 radio_add_preset();
1353 break;
1354 case ACTION_STD_CANCEL:
1355 result = 1;
1356 break;
1357 case ACTION_STD_OK:
1358 curr_preset = gui_synclist_get_sel_pos(&lists);
1359 curr_freq = presets[curr_preset].frequency;
1360 next_station(0);
1361 remember_frequency();
1362 result = 1;
1363 break;
1364 case ACTION_F3:
1365 case ACTION_STD_CONTEXT:
1366 selected_preset = gui_synclist_get_sel_pos(&lists);
1367 do_menu(&handle_radio_preset_menu, NULL);
1368 gui_synclist_speak_item(&lists);
1369 break;
1370 default:
1371 if(default_event_handler(action) == SYS_USB_CONNECTED)
1372 result = 2;
1375 return result - 1;
1378 void toggle_mono_mode(bool mono)
1380 tuner_set(RADIO_FORCE_MONO, mono);
1383 void set_radio_region(int region)
1385 #ifdef HAVE_RADIO_REGION
1386 tuner_set(RADIO_REGION, region);
1387 #endif
1388 next_station(0);
1389 remember_frequency();
1390 (void)region;
1393 MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
1394 MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
1396 #ifndef FM_MODE
1397 char* get_mode_text(int selected_item, void * data, char *buffer)
1399 (void)selected_item;
1400 (void)data;
1401 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_MODE),
1402 radio_mode ? str(LANG_PRESET) :
1403 str(LANG_RADIO_SCAN_MODE));
1404 return buffer;
1406 static int toggle_radio_mode(void)
1408 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
1409 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
1410 return 0;
1412 MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1413 toggle_radio_mode, NULL,
1414 get_mode_text, NULL, NULL, NULL, Icon_NOICON);
1415 #endif
1417 static int scan_presets(void)
1419 bool do_scan = true;
1421 if(num_presets > 0) /* Do that to avoid 2 questions. */
1422 do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS));
1424 if(do_scan)
1426 const struct fm_region_data * const fmr =
1427 &fm_region_data[global_settings.fm_region];
1429 char buf[MAX_FMPRESET_LEN + 1];
1430 int i;
1432 curr_freq = fmr->freq_min;
1433 num_presets = 0;
1434 memset(presets, 0, sizeof(presets));
1435 tuner_set(RADIO_MUTE, 1);
1437 while(curr_freq <= fmr->freq_max)
1439 int freq, frac;
1440 if (num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1441 break;
1443 freq = curr_freq / 10000;
1444 frac = freq % 100;
1445 freq /= 100;
1447 snprintf(buf, MAX_FMPRESET_LEN, str(LANG_FM_SCANNING), freq, frac);
1448 gui_syncsplash(0, buf);
1450 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
1452 /* add preset */
1453 presets[num_presets].name[0] = '\0';
1454 presets[num_presets].frequency = curr_freq;
1455 num_presets++;
1458 curr_freq += fmr->freq_step;
1461 if (radio_status == FMRADIO_PLAYING)
1462 tuner_set(RADIO_MUTE, 0);
1464 presets_changed = true;
1466 FOR_NB_SCREENS(i)
1468 gui_textarea_clear(&screens[i]);
1469 screen_set_xmargin(&screens[i],0);
1470 gui_textarea_update(&screens[i]);
1473 if(num_presets > 0)
1475 curr_freq = presets[0].frequency;
1476 radio_mode = RADIO_PRESET_MODE;
1477 presets_loaded = true;
1478 next_station(0);
1480 else
1482 /* Wrap it to beginning or we'll be past end of band */
1483 presets_loaded = false;
1484 next_station(1);
1487 return true;
1491 #ifdef HAVE_RECORDING
1493 #if defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC
1494 #define FM_RECORDING_SCREEN
1495 static int fm_recording_screen(void)
1497 bool ret;
1499 /* switch recording source to FMRADIO for the duration */
1500 int rec_source = global_settings.rec_source;
1501 global_settings.rec_source = AUDIO_SRC_FMRADIO;
1503 ret = recording_screen(true);
1505 /* safe to reset as changing sources is prohibited here */
1506 global_settings.rec_source = rec_source;
1508 return ret;
1511 #endif /* defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC */
1513 #if defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC
1514 #define FM_RECORDING_SETTINGS
1515 static int fm_recording_settings(void)
1517 bool ret = recording_menu(true);
1519 #if CONFIG_CODEC != SWCODEC
1520 if (!ret)
1522 struct audio_recording_options rec_options;
1523 rec_init_recording_options(&rec_options);
1524 rec_options.rec_source = AUDIO_SRC_LINEIN;
1525 rec_set_recording_options(&rec_options);
1527 #endif
1529 return ret;
1532 #endif /* defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC */
1533 #endif /* HAVE_RECORDING */
1535 #ifdef FM_RECORDING_SCREEN
1536 MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING),
1537 fm_recording_screen, NULL, NULL, Icon_Recording);
1538 #endif
1539 #ifdef FM_RECORDING_SETTINGS
1540 MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1541 fm_recording_settings, NULL, NULL, Icon_Recording);
1542 #endif
1543 #ifndef FM_PRESET
1544 MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
1545 handle_radio_presets, NULL, NULL, Icon_NOICON);
1546 #endif
1547 #ifndef FM_PRESET_ADD
1548 MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1549 radio_add_preset, NULL, NULL, Icon_NOICON);
1550 #endif
1553 MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1554 load_preset_list, NULL, NULL, Icon_NOICON);
1555 MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1556 save_preset_list, NULL, NULL, Icon_NOICON);
1557 MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1558 clear_preset_list, NULL, NULL, Icon_NOICON);
1559 MENUITEM_FUNCTION(scan_presets_item, 0, ID2P(LANG_FM_SCAN_PRESETS),
1560 scan_presets, NULL, NULL, Icon_NOICON);
1562 MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
1563 Icon_Radio_screen,
1564 #ifndef FM_PRESET
1565 &radio_presets_item,
1566 #endif
1567 #ifndef FM_PRESET_ADD
1568 &radio_addpreset_item,
1569 #endif
1570 &presetload_item, &presetsave_item, &presetclear_item,
1571 &force_mono,
1572 #ifndef FM_MODE
1573 &radio_mode_item,
1574 #endif
1575 &set_region, &sound_settings,
1576 #ifdef FM_RECORDING_SCREEN
1577 &recscreen_item,
1578 #endif
1579 #ifdef FM_RECORDING_SETTINGS
1580 &recsettings_item,
1581 #endif
1582 &scan_presets_item);
1583 /* main menu of the radio screen */
1584 static bool radio_menu(void)
1586 return do_menu(&radio_settings_menu, NULL) == MENU_ATTACHED_USB;
1589 #endif