The install window doesn't need to be wider than the other ones.
[Rockbox.git] / apps / recorder / radio.c
blobb95bffe6960ec1bb566ad06fb55d9fc402ed54dc
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 #include "recording.h"
49 #include "talk.h"
50 #include "tuner.h"
51 #include "power.h"
52 #include "sound.h"
53 #include "screen_access.h"
54 #include "statusbar.h"
55 #include "textarea.h"
56 #include "splash.h"
57 #include "yesno.h"
58 #include "buttonbar.h"
59 #include "power.h"
60 #include "tree.h"
61 #include "dir.h"
62 #include "action.h"
63 #include "list.h"
64 #include "menus/exported_menus.h"
65 #include "root_menu.h"
67 #if CONFIG_TUNER
69 #if CONFIG_KEYPAD == RECORDER_PAD
70 #define FM_RECORD
71 #define FM_PRESET_ADD
72 #define FM_PRESET_ACTION
73 #define FM_PRESET
74 #define FM_MODE
76 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
77 #define FM_PRESET
78 #define FM_MODE
79 #define FM_NEXT_PRESET
80 #define FM_PREV_PRESET
82 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
83 #define FM_PRESET
84 #define FM_MODE
85 /* This should be removeable if the whole tuning thing is sorted out since
86 proper tuning quiets the screen almost entirely in that extreme measures
87 have to be taken to hear any interference. */
88 #define HAVE_NOISY_IDLE_MODE
90 #elif CONFIG_KEYPAD == ONDIO_PAD
91 #define FM_RECORD_DBLPRE
92 #define FM_RECORD
93 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
94 #define FM_MENU
95 #define FM_PRESET
96 #define FM_STOP
97 #define FM_MODE
98 #define FM_EXIT
99 #define FM_PLAY
100 #endif
102 #define RADIO_SCAN_MODE 0
103 #define RADIO_PRESET_MODE 1
105 static int curr_preset = -1;
106 static int curr_freq;
107 static int radio_mode = RADIO_SCAN_MODE;
108 static int search_dir = 0;
110 static int radio_status = FMRADIO_OFF;
111 static bool in_screen = false;
113 #define MAX_PRESETS 64
114 static bool presets_loaded = false, presets_changed = false;
115 static struct fmstation presets[MAX_PRESETS];
117 static char filepreset[MAX_PATH]; /* preset filename variable */
119 static int num_presets = 0; /* The number of presets in the preset list */
121 static void radio_save_presets(void);
122 static int handle_radio_presets(void);
123 static bool radio_menu(void);
124 static int radio_add_preset(void);
125 static int save_preset_list(void);
126 static int load_preset_list(void);
127 static int clear_preset_list(void);
129 static int scan_presets(void);
131 /* Function to manipulate all yesno dialogues.
132 This function needs the output text as an argument. */
133 static bool yesno_pop(char* text)
135 int i;
136 char *lines[]={text};
137 struct text_message message={lines, 1};
138 bool ret = (gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
139 FOR_NB_SCREENS(i)
140 gui_textarea_clear(&screens[i]);
141 return ret;
144 void radio_init(void)
146 tuner_init();
147 radio_stop();
150 int get_radio_status(void)
152 return radio_status;
155 bool in_radio_screen(void)
157 return in_screen;
160 /* TODO: Move some more of the control functionality to an HAL and clean up the
161 mess */
163 /* secret flag for starting paused - prevents unmute */
164 #define FMRADIO_START_PAUSED 0x8000
165 void radio_start(void)
167 const struct fm_region_data *fmr;
168 bool start_paused;
170 if(radio_status == FMRADIO_PLAYING)
171 return;
173 fmr = &fm_region_data[global_settings.fm_region];
175 start_paused = radio_status & FMRADIO_START_PAUSED;
176 /* clear flag before any yielding */
177 radio_status &= ~FMRADIO_START_PAUSED;
179 if(radio_status == FMRADIO_OFF)
180 tuner_power(true);
182 curr_freq = global_status.last_frequency
183 * fmr->freq_step + fmr->freq_min;
185 tuner_set(RADIO_SLEEP, 0); /* wake up the tuner */
187 if(radio_status == FMRADIO_OFF)
189 #ifdef HAVE_RADIO_REGION
190 tuner_set(RADIO_REGION, global_settings.fm_region);
191 #endif
192 tuner_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
195 tuner_set(RADIO_FREQUENCY, curr_freq);
197 #ifdef HAVE_RADIO_MUTE_TIMEOUT
199 unsigned long mute_timeout = current_tick + HZ;
200 if (radio_status != FMRADIO_OFF)
202 /* paused */
203 mute_timeout += HZ;
206 while(!tuner_get(RADIO_STEREO) && !tuner_get(RADIO_TUNED))
208 if(TIME_AFTER(current_tick, mute_timeout))
209 break;
210 yield();
213 #endif
215 /* keep radio from sounding initially */
216 if(!start_paused)
217 tuner_set(RADIO_MUTE, 0);
219 radio_status = FMRADIO_PLAYING;
220 } /* radio_start */
222 void radio_pause(void)
224 if(radio_status == FMRADIO_PAUSED)
225 return;
227 if(radio_status == FMRADIO_OFF)
229 radio_status |= FMRADIO_START_PAUSED;
230 radio_start();
233 tuner_set(RADIO_MUTE, 1);
234 tuner_set(RADIO_SLEEP, 1);
236 radio_status = FMRADIO_PAUSED;
237 } /* radio_pause */
239 void radio_stop(void)
241 if(radio_status == FMRADIO_OFF)
242 return;
244 tuner_set(RADIO_MUTE, 1);
245 tuner_set(RADIO_SLEEP, 1); /* low power mode, if available */
246 radio_status = FMRADIO_OFF;
247 tuner_power(false); /* status update, power off if avail. */
248 } /* radio_stop */
250 bool radio_hardware_present(void)
252 return tuner_get(RADIO_PRESENT);
255 /* Keep freq on the grid for the current region */
256 static int snap_freq_to_grid(int freq)
258 const struct fm_region_data * const fmr =
259 &fm_region_data[global_settings.fm_region];
261 /* Range clamp if out of range or just round to nearest */
262 if (freq < fmr->freq_min)
263 freq = fmr->freq_min;
264 else if (freq > fmr->freq_max)
265 freq = fmr->freq_max;
266 else
267 freq = (freq - fmr->freq_min + fmr->freq_step/2) /
268 fmr->freq_step * fmr->freq_step + fmr->freq_min;
270 return freq;
273 /* Find a matching preset to freq */
274 static int find_preset(int freq)
276 int i;
277 if(num_presets < 1)
278 return -1;
279 for(i = 0;i < MAX_PRESETS;i++)
281 if(freq == presets[i].frequency)
282 return i;
285 return -1;
288 /* Return the first preset encountered in the search direction with
289 wraparound. */
290 static int find_closest_preset(int freq, int direction)
292 int i;
294 if (direction == 0) /* direction == 0 isn't really used */
295 return 0;
297 for (i = 0; i < MAX_PRESETS; i++)
299 int preset_frequency = presets[i].frequency;
301 if (preset_frequency == freq)
302 return i; /* Exact match = stop */
303 /* Stop when the preset frequency exeeds freq so that we can
304 pick the correct one based on direction */
305 if (preset_frequency > freq)
306 break;
309 /* wrap around depending on direction */
310 if (i == 0 || i >= num_presets - 1)
311 i = direction < 0 ? num_presets - 1 : 0;
312 else if (direction < 0)
313 i--; /* use previous */
315 return i;
318 static void remember_frequency(void)
320 const struct fm_region_data * const fmr =
321 &fm_region_data[global_settings.fm_region];
322 global_status.last_frequency = (curr_freq - fmr->freq_min)
323 / fmr->freq_step;
324 status_save();
327 static void next_preset(int direction)
329 if (num_presets < 1)
330 return;
332 if (curr_preset == -1)
333 curr_preset = find_closest_preset(curr_freq, direction);
334 else
335 curr_preset = (curr_preset + direction + num_presets) % num_presets;
337 /* Must stay on the current grid for the region */
338 curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
340 tuner_set(RADIO_FREQUENCY, curr_freq);
341 remember_frequency();
344 /* Step to the next or previous frequency */
345 static int step_freq(int freq, int direction)
347 const struct fm_region_data * const fmr =
348 &fm_region_data[global_settings.fm_region];
350 freq += direction*fmr->freq_step;
352 /* Wrap first or snapping to grid will not let us on the band extremes */
353 if (freq > fmr->freq_max)
354 freq = direction > 0 ? fmr->freq_min : fmr->freq_max;
355 else if (freq < fmr->freq_min)
356 freq = direction < 0 ? fmr->freq_max : fmr->freq_min;
357 else
358 freq = snap_freq_to_grid(freq);
360 return freq;
363 /* Step to the next or previous station */
364 static void next_station(int direction)
366 if (direction != 0 && radio_mode != RADIO_SCAN_MODE)
368 next_preset(direction);
369 return;
372 curr_freq = step_freq(curr_freq, direction);
374 if (radio_status == FMRADIO_PLAYING)
375 tuner_set(RADIO_MUTE, 1);
377 tuner_set(RADIO_FREQUENCY, curr_freq);
379 if (radio_status == FMRADIO_PLAYING)
380 tuner_set(RADIO_MUTE, 0);
382 curr_preset = find_preset(curr_freq);
383 remember_frequency();
386 /* Ends an in-progress search */
387 static void end_search(void)
389 if (search_dir != 0 && radio_status == FMRADIO_PLAYING)
390 tuner_set(RADIO_MUTE, 0);
391 search_dir = 0;
394 int radio_screen(void)
396 char buf[MAX_PATH];
397 bool done = false;
398 int ret_val = GO_TO_ROOT;
399 int button;
400 int i;
401 bool stereo = false, last_stereo = false;
402 int fh;
403 int top_of_screen = 0;
404 bool update_screen = true;
405 bool screen_freeze = false;
406 bool keep_playing = false;
407 bool statusbar = global_settings.statusbar;
408 #ifdef FM_RECORD_DBLPRE
409 int lastbutton = BUTTON_NONE;
410 unsigned long rec_lastclick = 0;
411 #endif
412 #if CONFIG_CODEC != SWCODEC
413 bool have_recorded = false;
414 int timeout = current_tick + HZ/10;
415 unsigned int seconds = 0;
416 unsigned int last_seconds = 0;
417 int hours, minutes;
418 struct audio_recording_options rec_options;
419 #endif /* CONFIG_CODEC != SWCODEC */
420 #ifndef HAVE_NOISY_IDLE_MODE
421 int button_timeout = current_tick + (2*HZ);
422 #endif
423 #ifdef HAS_BUTTONBAR
424 struct gui_buttonbar buttonbar;
425 gui_buttonbar_init(&buttonbar);
426 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
427 #endif
429 /* change status to "in screen" */
430 in_screen = true;
432 /* always display status bar in radio screen for now */
433 global_settings.statusbar = true;
434 FOR_NB_SCREENS(i)
436 gui_textarea_clear(&screens[i]);
437 screen_set_xmargin(&screens[i],0);
440 gui_syncstatusbar_draw(&statusbars,true);
442 fh = font_get(FONT_UI)->height;
444 /* Adjust for font size, trying to center the information vertically */
445 if(fh < 10)
446 top_of_screen = 1;
448 if(num_presets <= 0)
450 memset(presets, 0, sizeof(presets));
451 radio_load_presets(global_settings.fmr_file);
454 if(radio_status == FMRADIO_OFF)
455 audio_stop();
456 #ifndef SIMULATOR
458 #if CONFIG_CODEC != SWCODEC
459 if(rec_create_directory() > 0)
460 have_recorded = true;
462 audio_init_recording(talk_get_bufsize());
464 sound_settings_apply();
465 /* Yes, we use the D/A for monitoring */
466 peak_meter_playback(true);
468 peak_meter_enabled = true;
470 rec_init_recording_options(&rec_options);
471 rec_options.rec_source = AUDIO_SRC_LINEIN;
472 rec_set_recording_options(&rec_options);
474 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
475 sound_default(SOUND_RIGHT_GAIN), AUDIO_GAIN_LINEIN);
477 #endif /* CONFIG_CODEC != SWCODEC */
478 #endif /* ndef SIMULATOR */
480 /* turn on radio */
481 #if CONFIG_CODEC == SWCODEC
482 audio_set_input_source(AUDIO_SRC_FMRADIO,
483 (radio_status == FMRADIO_PAUSED) ?
484 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
485 #else
486 if (radio_status == FMRADIO_OFF)
487 radio_start();
488 #endif
490 if(num_presets < 1 && yesno_pop(ID2P(LANG_FM_FIRST_AUTOSCAN)))
491 scan_presets();
493 curr_preset = find_preset(curr_freq);
494 if(curr_preset != -1)
495 radio_mode = RADIO_PRESET_MODE;
497 #ifdef HAS_BUTTONBAR
498 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
499 str(LANG_PRESET), str(LANG_FM_BUTTONBAR_RECORD));
500 #endif
502 #ifndef HAVE_NOISY_IDLE_MODE
503 cpu_idle_mode(true);
504 #endif
506 while(!done)
508 if(search_dir != 0)
510 curr_freq = step_freq(curr_freq, search_dir);
511 update_screen = true;
513 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
515 curr_preset = find_preset(curr_freq);
516 remember_frequency();
517 end_search();
520 trigger_cpu_boost();
523 #if CONFIG_CODEC != SWCODEC
524 /* TODO: Can we timeout at HZ when recording since peaks aren't
525 displayed? This should quiet recordings too. */
526 button = get_action(CONTEXT_FM,
527 update_screen ? TIMEOUT_NOBLOCK : HZ / PEAK_METER_FPS);
528 #else
529 button = get_action(CONTEXT_FM,
530 update_screen ? TIMEOUT_NOBLOCK : HZ);
531 #endif
533 #ifndef HAVE_NOISY_IDLE_MODE
534 if (button != ACTION_NONE)
536 cpu_idle_mode(false);
537 button_timeout = current_tick + (2*HZ);
539 #endif
540 switch(button)
542 case ACTION_FM_STOP:
543 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
544 if(audio_status() == AUDIO_STATUS_RECORD)
546 audio_stop();
548 else
549 #endif
551 done = true;
552 if(presets_changed)
554 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
556 if(filepreset[0] == '\0')
557 save_preset_list();
558 else
559 radio_save_presets();
562 /* Clear the preset list on exit. */
563 clear_preset_list();
565 update_screen = true;
566 break;
568 #ifdef FM_RECORD
569 case ACTION_FM_RECORD:
570 #ifdef FM_RECORD_DBLPRE
571 if (lastbutton != ACTION_FM_RECORD_DBLPRE)
573 rec_lastclick = 0;
574 break;
576 if (current_tick - rec_lastclick > HZ/2)
578 rec_lastclick = current_tick;
579 break;
581 #endif /* FM_RECORD_DBLPRE */
582 #ifndef SIMULATOR
583 if(audio_status() == AUDIO_STATUS_RECORD)
585 rec_command(RECORDING_CMD_START_NEWFILE);
586 update_screen = true;
588 else
590 have_recorded = true;
591 rec_command(RECORDING_CMD_START);
592 update_screen = true;
594 #endif /* SIMULATOR */
595 last_seconds = 0;
596 break;
597 #endif /* #ifdef FM_RECORD */
599 case ACTION_FM_EXIT:
600 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
601 if(audio_status() == AUDIO_STATUS_RECORD)
602 audio_stop();
603 #endif
604 keep_playing = true;
605 done = true;
606 ret_val = GO_TO_ROOT;
607 if(presets_changed)
609 if(yesno_pop(ID2P(LANG_FM_SAVE_CHANGES)))
611 if(filepreset[0] == '\0')
612 save_preset_list();
613 else
614 radio_save_presets();
618 /* Clear the preset list on exit. */
619 clear_preset_list();
621 break;
623 case ACTION_STD_PREV:
624 case ACTION_STD_NEXT:
625 next_station(button == ACTION_STD_PREV ? -1 : 1);
626 end_search();
627 update_screen = true;
628 break;
630 case ACTION_STD_PREVREPEAT:
631 case ACTION_STD_NEXTREPEAT:
633 int dir = search_dir;
634 search_dir = button == ACTION_STD_PREVREPEAT ? -1 : 1;
635 if (radio_mode != RADIO_SCAN_MODE)
637 next_preset(search_dir);
638 end_search();
639 update_screen = true;
641 else if (dir == 0)
643 /* Starting auto scan */
644 tuner_set(RADIO_MUTE, 1);
645 update_screen = true;
647 break;
650 case ACTION_SETTINGS_INC:
651 case ACTION_SETTINGS_INCREPEAT:
652 global_settings.volume++;
653 setvol();
654 update_screen = true;
655 break;
657 case ACTION_SETTINGS_DEC:
658 case ACTION_SETTINGS_DECREPEAT:
659 global_settings.volume--;
660 setvol();
661 update_screen = true;
662 break;
664 case ACTION_FM_PLAY:
665 if (radio_status == FMRADIO_PLAYING)
666 radio_pause();
667 else
668 radio_start();
670 update_screen = true;
671 break;
673 case ACTION_FM_MENU:
674 radio_menu();
675 curr_preset = find_preset(curr_freq);
676 FOR_NB_SCREENS(i){
677 struct screen *sc = &screens[i];
678 gui_textarea_clear(sc);
679 screen_set_xmargin(sc, 0);
681 #ifdef HAS_BUTTONBAR
682 gui_buttonbar_set(&buttonbar, str(LANG_BUTTONBAR_MENU),
683 str(LANG_PRESET),
684 str(LANG_FM_BUTTONBAR_RECORD));
685 #endif
686 update_screen = true;
687 break;
689 #ifdef FM_PRESET
690 case ACTION_FM_PRESET:
691 if(num_presets < 1)
693 gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS));
694 update_screen = true;
695 FOR_NB_SCREENS(i)
697 struct screen *sc = &screens[i];
698 gui_textarea_clear(sc);
699 screen_set_xmargin(sc, 0);
700 gui_textarea_update(sc);
703 break;
705 handle_radio_presets();
706 FOR_NB_SCREENS(i)
708 struct screen *sc = &screens[i];
709 gui_textarea_clear(sc);
710 screen_set_xmargin(sc, 0);
711 gui_textarea_update(sc);
713 #ifdef HAS_BUTTONBAR
714 gui_buttonbar_set(&buttonbar,
715 str(LANG_BUTTONBAR_MENU),
716 str(LANG_PRESET),
717 str(LANG_FM_BUTTONBAR_RECORD));
718 #endif
719 update_screen = true;
720 break;
721 #endif /* FM_PRESET */
723 #ifdef FM_FREEZE
724 case ACTION_FM_FREEZE:
725 if(!screen_freeze)
727 gui_syncsplash(HZ, str(LANG_FM_FREEZE));
728 screen_freeze = true;
730 else
732 update_screen = true;
733 screen_freeze = false;
735 break;
736 #endif /* FM_FREEZE */
738 case SYS_USB_CONNECTED:
739 #if CONFIG_CODEC != SWCODEC
740 /* Only accept USB connection when not recording */
741 if(audio_status() != AUDIO_STATUS_RECORD)
742 #endif
744 default_event_handler(SYS_USB_CONNECTED);
745 screen_freeze = true; /* Cosmetic: makes sure the
746 radio screen doesn't redraw */
747 done = true;
749 break;
751 #ifdef FM_MODE
752 case ACTION_FM_MODE:
753 if(radio_mode == RADIO_SCAN_MODE)
755 /* Force scan mode if there are no presets. */
756 if(num_presets > 0)
757 radio_mode = RADIO_PRESET_MODE;
759 else
760 radio_mode = RADIO_SCAN_MODE;
761 update_screen = true;
762 break;
763 #endif /* FM_MODE */
765 #ifdef FM_NEXT_PRESET
766 case ACTION_FM_NEXT_PRESET:
767 next_preset(1);
768 end_search();
769 update_screen = true;
770 break;
771 #endif
773 #ifdef FM_PREV_PRESET
774 case ACTION_FM_PREV_PRESET:
775 next_preset(-1);
776 end_search();
777 update_screen = true;
778 break;
779 #endif
781 default:
782 default_event_handler(button);
783 break;
784 } /*switch(button)*/
786 #ifdef FM_RECORD_DBLPRE
787 if (button != ACTION_NONE)
788 lastbutton = button;
789 #endif
791 #if CONFIG_CODEC != SWCODEC
792 peak_meter_peek();
793 #endif
795 if(!screen_freeze)
797 /* Only display the peak meter when not recording */
798 #if CONFIG_CODEC != SWCODEC
799 if(!audio_status())
801 FOR_NB_SCREENS(i)
803 peak_meter_screen(&screens[i],0,
804 STATUSBAR_HEIGHT + fh*(top_of_screen + 4), fh);
805 screens[i].update_rect(0, STATUSBAR_HEIGHT + fh*(top_of_screen + 4),
806 screens[i].width, fh);
810 if(TIME_AFTER(current_tick, timeout))
812 timeout = current_tick + HZ;
813 #else /* SWCODEC */
815 #endif /* CONFIG_CODEC == SWCODEC */
817 /* keep "mono" from always being displayed when paused */
818 if (radio_status != FMRADIO_PAUSED)
820 stereo = tuner_get(RADIO_STEREO) &&
821 !global_settings.fm_force_mono;
823 if(stereo != last_stereo)
825 update_screen = true;
826 last_stereo = stereo;
831 #if CONFIG_CODEC != SWCODEC && !defined(SIMULATOR)
832 seconds = audio_recorded_time() / HZ;
833 if (update_screen || seconds > last_seconds)
835 last_seconds = seconds;
836 #else
837 if (update_screen)
839 #endif
840 int freq;
842 FOR_NB_SCREENS(i)
843 screens[i].setfont(FONT_UI);
845 snprintf(buf, 128, curr_preset >= 0 ? "%d. %s" : " ",
846 curr_preset + 1, presets[curr_preset].name);
848 FOR_NB_SCREENS(i)
849 screens[i].puts_scroll(0, top_of_screen, buf);
851 freq = curr_freq / 10000;
852 snprintf(buf, 128, str(LANG_FM_STATION), freq / 100, freq % 100);
853 FOR_NB_SCREENS(i)
854 screens[i].puts_scroll(0, top_of_screen + 1, buf);
856 snprintf(buf, 128, stereo?str(LANG_CHANNEL_STEREO):
857 str(LANG_CHANNEL_MONO));
858 FOR_NB_SCREENS(i)
859 screens[i].puts_scroll(0, top_of_screen + 2, buf);
861 snprintf(buf, 128, "%s %s", str(LANG_MODE),
862 radio_mode ? str(LANG_PRESET) :
863 str(LANG_RADIO_SCAN_MODE));
864 FOR_NB_SCREENS(i)
865 screens[i].puts_scroll(0, top_of_screen + 3, buf);
867 #if CONFIG_CODEC != SWCODEC
868 if(audio_status() == AUDIO_STATUS_RECORD)
870 hours = seconds / 3600;
871 minutes = (seconds - (hours * 3600)) / 60;
872 snprintf(buf, 32, "%s %02d:%02d:%02d",
873 str(LANG_RECORDING_TIME),
874 hours, minutes, seconds%60);
875 FOR_NB_SCREENS(i)
876 screens[i].puts_scroll(0, top_of_screen + 4, buf);
878 else
880 if(rec_options.rec_prerecord_time)
882 snprintf(buf, 32, "%s %02d",
883 str(LANG_RECORD_PRERECORD), seconds%60);
884 FOR_NB_SCREENS(i)
885 screens[i].puts_scroll(0, top_of_screen + 4, buf);
888 #endif /* CONFIG_CODEC != SWCODEC */
890 #ifdef HAS_BUTTONBAR
891 gui_buttonbar_draw(&buttonbar);
892 #endif
893 FOR_NB_SCREENS(i)
894 gui_textarea_update(&screens[i]);
896 /* Only force the redraw if update_screen is true */
897 gui_syncstatusbar_draw(&statusbars,true);
900 update_screen = false;
902 #if CONFIG_CODEC != SWCODEC
903 if(audio_status() & AUDIO_STATUS_ERROR)
905 done = true;
907 #endif
909 #ifndef HAVE_NOISY_IDLE_MODE
910 if (TIME_AFTER(current_tick, button_timeout))
912 cpu_idle_mode(true);
914 #endif
915 } /*while(!done)*/
917 #ifndef SIMULATOR
918 #if CONFIG_CODEC != SWCODEC
919 if(audio_status() & AUDIO_STATUS_ERROR)
921 gui_syncsplash(0, str(LANG_DISK_FULL));
922 gui_syncstatusbar_draw(&statusbars,true);
923 FOR_NB_SCREENS(i)
924 gui_textarea_update(&screens[i]);
925 audio_error_clear();
927 while(1)
929 button = get_action(CONTEXT_FM, TIMEOUT_BLOCK);
930 if(button == ACTION_FM_STOP)
931 break;
935 audio_init_playback();
936 #endif /* CONFIG_CODEC != SWCODEC */
938 sound_settings_apply();
939 #endif /* SIMULATOR */
941 if(keep_playing)
943 /* Catch FMRADIO_PLAYING status for the sim. */
944 #ifndef SIMULATOR
945 #if CONFIG_CODEC != SWCODEC
946 /* Enable the Left and right A/D Converter */
947 audio_set_recording_gain(sound_default(SOUND_LEFT_GAIN),
948 sound_default(SOUND_RIGHT_GAIN),
949 AUDIO_GAIN_LINEIN);
950 mas_codec_writereg(6, 0x4000);
951 #endif
952 end_search();
953 #endif /* SIMULATOR */
955 else
957 #if CONFIG_CODEC == SWCODEC
958 audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
959 #else
960 radio_stop();
961 #endif
964 #ifndef HAVE_NOISY_IDLE_MODE
965 cpu_idle_mode(false);
966 #endif
968 /* restore status bar settings */
969 global_settings.statusbar = statusbar;
971 in_screen = false;
972 #if CONFIG_CODEC != SWCODEC
973 return have_recorded;
974 #else
975 return false;
976 #endif
977 } /* radio_screen */
979 static void radio_save_presets(void)
981 int fd;
982 int i;
984 fd = creat(filepreset);
985 if(fd >= 0)
987 for(i = 0;i < num_presets;i++)
989 fdprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
991 close(fd);
993 if(!strncasecmp(FMPRESET_PATH, filepreset, strlen(FMPRESET_PATH)))
994 set_file(filepreset, global_settings.fmr_file, MAX_FILENAME);
995 presets_changed = false;
997 else
999 gui_syncsplash(HZ, ID2P(LANG_FM_PRESET_SAVE_FAILED));
1003 void radio_load_presets(char *filename)
1005 int fd;
1006 int rc;
1007 char buf[128];
1008 char *freq;
1009 char *name;
1010 bool done = false;
1011 int f;
1013 memset(presets, 0, sizeof(presets));
1014 num_presets = 0;
1016 /* No Preset in configuration. */
1017 if(filename[0] == '\0')
1019 filepreset[0] = '\0';
1020 return;
1022 /* Temporary preset, loaded until player shuts down. */
1023 else if(filename[0] == '/')
1024 strncpy(filepreset, filename, sizeof(filepreset));
1025 /* Preset from default directory. */
1026 else
1027 snprintf(filepreset, sizeof(filepreset), "%s/%s.fmr",
1028 FMPRESET_PATH, filename);
1030 fd = open(filepreset, O_RDONLY);
1031 if(fd >= 0)
1033 while(!done && num_presets < MAX_PRESETS)
1035 rc = read_line(fd, buf, 128);
1036 if(rc > 0)
1038 if(settings_parseline(buf, &freq, &name))
1040 f = atoi(freq);
1041 if(f) /* For backwards compatibility */
1043 struct fmstation * const fms = &presets[num_presets];
1044 fms->frequency = f;
1045 strncpy(fms->name, name, MAX_FMPRESET_LEN);
1046 fms->name[MAX_FMPRESET_LEN] = '\0';
1047 num_presets++;
1051 else
1052 done = true;
1054 close(fd);
1056 else /* invalid file name? */
1057 filepreset[0] = '\0';
1059 presets_loaded = num_presets > 0;
1060 presets_changed = false;
1064 static int radio_add_preset(void)
1066 char buf[MAX_FMPRESET_LEN + 1];
1068 if(num_presets < MAX_PRESETS)
1070 memset(buf, 0, MAX_FMPRESET_LEN);
1072 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1074 struct fmstation * const fms = &presets[num_presets];
1075 buf[MAX_FMPRESET_LEN] = '\0';
1076 strcpy(fms->name, buf);
1077 fms->frequency = curr_freq;
1078 num_presets++;
1079 presets_changed = true;
1080 presets_loaded = num_presets > 0;
1083 else
1085 gui_syncsplash(HZ, ID2P(LANG_FM_NO_FREE_PRESETS));
1087 return true;
1090 /* needed to know which preset we are edit/delete-ing */
1091 static int selected_preset = -1;
1092 static int radio_edit_preset(void)
1094 char buf[MAX_FMPRESET_LEN + 1];
1096 if (num_presets > 0)
1098 struct fmstation * const fms = &presets[selected_preset];
1100 strncpy(buf, fms->name, MAX_FMPRESET_LEN);
1102 if (!kbd_input(buf, MAX_FMPRESET_LEN))
1104 buf[MAX_FMPRESET_LEN] = '\0';
1105 strcpy(fms->name, buf);
1106 presets_changed = true;
1110 return 1;
1113 static int radio_delete_preset(void)
1115 if (num_presets > 0)
1117 struct fmstation * const fms = &presets[selected_preset];
1119 if (selected_preset >= --num_presets)
1120 selected_preset = num_presets - 1;
1122 memmove(fms, fms + 1, (uintptr_t)(fms + num_presets) -
1123 (uintptr_t)fms);
1127 /* Don't ask to save when all presets are deleted. */
1128 presets_changed = num_presets > 0;
1130 if (!presets_changed)
1132 /* The preset list will be cleared, switch to Scan Mode. */
1133 radio_mode = RADIO_SCAN_MODE;
1134 presets_loaded = false;
1137 return 1;
1140 static int load_preset_list(void)
1142 return !rockbox_browse(FMPRESET_PATH, SHOW_FMR);
1145 static int save_preset_list(void)
1147 if(num_presets > 0)
1149 bool bad_file_name = true;
1151 if(!opendir(FMPRESET_PATH)) /* Check if there is preset folder */
1152 mkdir(FMPRESET_PATH);
1154 create_numbered_filename(filepreset, FMPRESET_PATH, "preset",
1155 ".fmr", 2 IF_CNFN_NUM_(, NULL));
1157 while(bad_file_name)
1159 if(!kbd_input(filepreset, sizeof(filepreset)))
1161 /* check the name: max MAX_FILENAME (20) chars */
1162 char* p2;
1163 char* p1;
1164 int len;
1165 p1 = strrchr(filepreset, '/');
1166 p2 = p1;
1167 while((p1) && (*p2) && (*p2 != '.'))
1168 p2++;
1169 len = (int)(p2-p1) - 1;
1170 if((!p1) || (len > MAX_FILENAME) || (len == 0))
1172 /* no slash, too long or too short */
1173 gui_syncsplash(HZ, ID2P(LANG_INVALID_FILENAME));
1175 else
1177 /* add correct extension (easier to always write)
1178 at this point, p2 points to 0 or the extension dot */
1179 *p2 = '\0';
1180 strcat(filepreset,".fmr");
1181 bad_file_name = false;
1182 radio_save_presets();
1185 else
1187 /* user aborted */
1188 return false;
1192 else
1193 gui_syncsplash(HZ, ID2P(LANG_FM_NO_PRESETS));
1195 return true;
1198 static int clear_preset_list(void)
1200 /* Clear all the preset entries */
1201 memset(presets, 0, sizeof (presets));
1203 num_presets = 0;
1204 presets_loaded = false;
1205 /* The preset list will be cleared switch to Scan Mode. */
1206 radio_mode = RADIO_SCAN_MODE;
1208 presets_changed = false; /* Don't ask to save when clearing the list. */
1210 return true;
1213 MENUITEM_FUNCTION(radio_edit_preset_item, MENU_FUNC_CHECK_RETVAL,
1214 ID2P(LANG_FM_EDIT_PRESET),
1215 radio_edit_preset, NULL, NULL, Icon_NOICON);
1216 MENUITEM_FUNCTION(radio_delete_preset_item, MENU_FUNC_CHECK_RETVAL,
1217 ID2P(LANG_FM_DELETE_PRESET),
1218 radio_delete_preset, NULL, NULL, Icon_NOICON);
1219 int radio_preset_callback(int action, const struct menu_item_ex *this_item)
1221 if (action == ACTION_STD_OK)
1222 action = ACTION_EXIT_AFTER_THIS_MENUITEM;
1223 return action;
1224 (void)this_item;
1226 MAKE_MENU(handle_radio_preset_menu, ID2P(LANG_PRESET),
1227 radio_preset_callback, Icon_NOICON, &radio_edit_preset_item,
1228 &radio_delete_preset_item);
1229 /* present a list of preset stations */
1230 char * presets_get_name(int selected_item, void * data, char *buffer)
1232 (void)data;
1233 (void)buffer;
1234 return presets[selected_item].name;
1237 static int handle_radio_presets(void)
1239 struct gui_synclist lists;
1240 int result = 0;
1241 int action = ACTION_NONE;
1242 #ifdef HAS_BUTTONBAR
1243 struct gui_buttonbar buttonbar;
1244 #endif
1246 if(presets_loaded == false)
1247 return result;
1249 #ifdef HAS_BUTTONBAR
1250 gui_buttonbar_init(&buttonbar);
1251 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
1252 gui_buttonbar_set(&buttonbar, str(LANG_FM_BUTTONBAR_ADD),
1253 str(LANG_FM_BUTTONBAR_EXIT),
1254 str(LANG_FM_BUTTONBAR_ACTION));
1255 gui_buttonbar_draw(&buttonbar);
1256 #endif
1257 gui_synclist_init(&lists, presets_get_name, NULL, false, 1);
1258 gui_synclist_set_title(&lists, str(LANG_PRESET), NOICON);
1259 gui_synclist_set_icon_callback(&lists, NULL);
1260 gui_synclist_set_nb_items(&lists, num_presets);
1261 gui_synclist_select_item(&lists, curr_preset<0 ? 0 : curr_preset);
1263 while (result == 0)
1265 gui_synclist_draw(&lists);
1266 gui_syncstatusbar_draw(&statusbars, true);
1267 action = get_action(CONTEXT_STD, HZ);
1269 gui_synclist_do_button(&lists, action, LIST_WRAP_UNLESS_HELD);
1270 switch (action)
1272 case ACTION_STD_MENU:
1273 radio_add_preset();
1274 break;
1275 case ACTION_STD_CANCEL:
1276 result = 1;
1277 break;
1278 case ACTION_STD_OK:
1279 curr_preset = gui_synclist_get_sel_pos(&lists);
1280 curr_freq = presets[curr_preset].frequency;
1281 next_station(0);
1282 remember_frequency();
1283 result = 1;
1284 break;
1285 case ACTION_F3:
1286 case ACTION_STD_CONTEXT:
1287 selected_preset = gui_synclist_get_sel_pos(&lists);
1288 do_menu(&handle_radio_preset_menu, NULL);
1289 break;
1290 default:
1291 if(default_event_handler(action) == SYS_USB_CONNECTED)
1292 result = 2;
1295 return result - 1;
1298 void toggle_mono_mode(bool mono)
1300 tuner_set(RADIO_FORCE_MONO, mono);
1303 void set_radio_region(int region)
1305 #ifdef HAVE_RADIO_REGION
1306 tuner_set(RADIO_REGION, region);
1307 #endif
1308 next_station(0);
1309 remember_frequency();
1310 (void)region;
1313 MENUITEM_SETTING(set_region, &global_settings.fm_region, NULL);
1314 MENUITEM_SETTING(force_mono, &global_settings.fm_force_mono, NULL);
1316 #ifndef FM_MODE
1317 char* get_mode_text(int selected_item, void * data, char *buffer)
1319 (void)selected_item;
1320 (void)data;
1321 snprintf(buffer, MAX_PATH, "%s %s", str(LANG_MODE),
1322 radio_mode ? str(LANG_PRESET) :
1323 str(LANG_RADIO_SCAN_MODE));
1324 return buffer;
1326 static int toggle_radio_mode(void)
1328 radio_mode = (radio_mode == RADIO_SCAN_MODE) ?
1329 RADIO_PRESET_MODE : RADIO_SCAN_MODE;
1330 return 0;
1332 MENUITEM_FUNCTION_DYNTEXT(radio_mode_item, 0,
1333 toggle_radio_mode, NULL,
1334 get_mode_text, NULL, NULL, Icon_NOICON);
1335 #endif
1337 static int scan_presets(void)
1339 bool do_scan = true;
1341 if(num_presets > 0) /* Do that to avoid 2 questions. */
1342 do_scan = yesno_pop(ID2P(LANG_FM_CLEAR_PRESETS));
1344 if(do_scan)
1346 const struct fm_region_data * const fmr =
1347 &fm_region_data[global_settings.fm_region];
1349 char buf[MAX_FMPRESET_LEN + 1];
1350 int i;
1352 curr_freq = fmr->freq_min;
1353 num_presets = 0;
1354 memset(presets, 0, sizeof(presets));
1355 tuner_set(RADIO_MUTE, 1);
1357 while(curr_freq <= fmr->freq_max)
1359 int freq, frac;
1360 if (num_presets >= MAX_PRESETS || action_userabort(TIMEOUT_NOBLOCK))
1361 break;
1363 freq = curr_freq / 10000;
1364 frac = freq % 100;
1365 freq /= 100;
1367 snprintf(buf, MAX_FMPRESET_LEN, str(LANG_FM_SCANNING), freq, frac);
1368 gui_syncsplash(0, buf);
1370 if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
1372 /* add preset */
1373 snprintf(buf, MAX_FMPRESET_LEN,
1374 str(LANG_FM_DEFAULT_PRESET_NAME), freq, frac);
1375 strcpy(presets[num_presets].name,buf);
1376 presets[num_presets].frequency = curr_freq;
1377 num_presets++;
1380 curr_freq += fmr->freq_step;
1383 if (radio_status == FMRADIO_PLAYING)
1384 tuner_set(RADIO_MUTE, 0);
1386 presets_changed = true;
1388 FOR_NB_SCREENS(i)
1390 gui_textarea_clear(&screens[i]);
1391 screen_set_xmargin(&screens[i],0);
1392 gui_textarea_update(&screens[i]);
1395 if(num_presets > 0)
1397 curr_freq = presets[0].frequency;
1398 radio_mode = RADIO_PRESET_MODE;
1399 presets_loaded = true;
1400 next_station(0);
1402 else
1404 /* Wrap it to beginning or we'll be past end of band */
1405 presets_loaded = false;
1406 next_station(1);
1409 return true;
1413 #ifdef HAVE_RECORDING
1415 #if defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC
1416 #define FM_RECORDING_SCREEN
1417 static int fm_recording_screen(void)
1419 bool ret;
1421 /* switch recording source to FMRADIO for the duration */
1422 int rec_source = global_settings.rec_source;
1423 global_settings.rec_source = AUDIO_SRC_FMRADIO;
1425 ret = recording_screen(true);
1427 /* safe to reset as changing sources is prohibited here */
1428 global_settings.rec_source = rec_source;
1430 return ret;
1433 #endif /* defined(HAVE_FMRADIO_REC) && CONFIG_CODEC == SWCODEC */
1435 #if defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC
1436 #define FM_RECORDING_SETTINGS
1437 static int fm_recording_settings(void)
1439 bool ret = recording_menu(true);
1441 #if CONFIG_CODEC != SWCODEC
1442 if (!ret)
1444 struct audio_recording_options rec_options;
1445 rec_init_recording_options(&rec_options);
1446 rec_options.rec_source = AUDIO_SRC_LINEIN;
1447 rec_set_recording_options(&rec_options);
1449 #endif
1451 return ret;
1454 #endif /* defined(HAVE_FMRADIO_REC) || CONFIG_CODEC != SWCODEC */
1455 #endif /* HAVE_RECORDING */
1457 #ifdef FM_RECORDING_SCREEN
1458 MENUITEM_FUNCTION(recscreen_item, 0, ID2P(LANG_RECORDING),
1459 fm_recording_screen, NULL, NULL, Icon_Recording);
1460 #endif
1461 #ifdef FM_RECORDING_SETTINGS
1462 MENUITEM_FUNCTION(recsettings_item, 0, ID2P(LANG_RECORDING_SETTINGS),
1463 fm_recording_settings, NULL, NULL, Icon_Recording);
1464 #endif
1465 #ifndef FM_PRESET
1466 MENUITEM_FUNCTION(radio_presets_item, 0, ID2P(LANG_PRESET),
1467 handle_radio_presets, NULL, NULL, Icon_NOICON);
1468 #endif
1469 #ifndef FM_PRESET_ADD
1470 MENUITEM_FUNCTION(radio_addpreset_item, 0, ID2P(LANG_FM_ADD_PRESET),
1471 radio_add_preset, NULL, NULL, Icon_NOICON);
1472 #endif
1475 MENUITEM_FUNCTION(presetload_item, 0, ID2P(LANG_FM_PRESET_LOAD),
1476 load_preset_list, NULL, NULL, Icon_NOICON);
1477 MENUITEM_FUNCTION(presetsave_item, 0, ID2P(LANG_FM_PRESET_SAVE),
1478 save_preset_list, NULL, NULL, Icon_NOICON);
1479 MENUITEM_FUNCTION(presetclear_item, 0, ID2P(LANG_FM_PRESET_CLEAR),
1480 clear_preset_list, NULL, NULL, Icon_NOICON);
1481 MENUITEM_FUNCTION(scan_presets_item, 0, ID2P(LANG_FM_SCAN_PRESETS),
1482 scan_presets, NULL, NULL, Icon_NOICON);
1484 MAKE_MENU(radio_settings_menu, ID2P(LANG_FM_MENU), NULL,
1485 Icon_Radio_screen,
1486 #ifndef FM_PRESET
1487 &radio_presets_item,
1488 #endif
1489 #ifndef FM_PRESET_ADD
1490 &radio_addpreset_item,
1491 #endif
1492 &presetload_item, &presetsave_item, &presetclear_item,
1493 &force_mono,
1494 #ifndef FM_MODE
1495 &radio_mode_item,
1496 #endif
1497 &set_region, &sound_settings,
1498 #ifdef FM_RECORDING_SCREEN
1499 &recscreen_item,
1500 #endif
1501 #ifdef FM_RECORDING_SETTINGS
1502 &recsettings_item,
1503 #endif
1504 &scan_presets_item);
1505 /* main menu of the radio screen */
1506 static bool radio_menu(void)
1508 return do_menu(&radio_settings_menu, NULL) == MENU_ATTACHED_USB;
1511 #endif