* removed some more unused stuff in the simulator makefile
[kugel-rb.git] / apps / recorder / radio.c
blobaf39124b9bf0439d8547790f171ac932f10b28e3
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 "sprintf.h"
24 #include "lcd.h"
25 #include "mas.h"
26 #include "settings.h"
27 #include "button.h"
28 #include "fmradio.h"
29 #include "status.h"
30 #include "kernel.h"
31 #include "mpeg.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"
51 #ifdef HAVE_FMRADIO
53 #define MAX_FREQ (108000000)
54 #define MIN_FREQ (87500000)
55 #define PLL_FREQ_STEP 10000
56 #define FREQ_STEP 100000
58 static int curr_preset = -1;
59 static int curr_freq = 99400000;
60 static int pll_cnt;
62 #define MAX_PRESETS 32
63 static bool presets_loaded = false;
64 static struct fmstation presets[MAX_PRESETS];
66 static char default_filename[] = "/.rockbox/fm-presets-default.fmr";
68 int debug_fm_detection;
70 void radio_load_presets(void);
71 bool radio_preset_select(void);
72 bool radio_menu(void);
74 void radio_stop(void)
76 /* Mute the FM radio */
77 fmradio_set(1, 0x100003);
81 bool radio_hardware_present(void)
83 int val;
85 fmradio_set(2, 0x140885); /* 5kHz, 7.2MHz crystal, test mode 1 */
86 val = fmradio_read(0);
87 debug_fm_detection = val;
88 if(val == 0x140885)
89 return true;
90 else
91 return false;
94 void radio_set_frequency(int freq)
96 /* We add the standard Intermediate Frequency 10.7MHz before calculating
97 ** the divisor
98 ** The reference frequency is set to 50kHz, and the VCO output is prescaled
99 ** by 2.
102 pll_cnt = (freq + 10700000) / (PLL_FREQ_STEP/2) / 2;
104 /* 0x100000 == FM mode
105 ** 0x000002 == Microprocessor controlled Mute
107 fmradio_set(1, 0x100002 | pll_cnt << 3);
110 static int find_preset(int freq)
112 int i;
113 for(i = 0;i < MAX_PRESETS;i++)
115 if(freq == presets[i].frequency)
116 return i;
119 return -1;
122 bool radio_screen(void)
124 char buf[MAX_PATH];
125 bool done = false;
126 int button;
127 int val;
128 int freq;
129 int i_freq;
130 bool stereo = false;
131 int search_dir = 0;
132 int fw, fh;
133 int last_stereo_status = false;
134 int top_of_screen = 0;
135 bool update_screen = true;
136 int timeout = current_tick + HZ/10;
137 bool screen_freeze = false;
138 bool have_recorded = false;
139 unsigned int seconds;
140 unsigned int last_seconds = 0;
141 int hours, minutes;
142 bool keep_playing = false;
144 lcd_clear_display();
145 lcd_setmargins(0, 8);
146 status_draw(true);
147 fmradio_set_status(FMRADIO_PLAYING);
149 font_get(FONT_UI);
150 lcd_getstringsize("M", &fw, &fh);
152 /* Adjust for font size, trying to center the information vertically */
153 if(fh < 10)
154 top_of_screen = 1;
156 radio_load_presets();
158 mpeg_stop();
160 mpeg_init_recording();
162 mpeg_sound_channel_config(global_settings.channel_config);
163 mpeg_sound_set(SOUND_BASS, global_settings.bass);
164 mpeg_sound_set(SOUND_TREBLE, global_settings.treble);
165 mpeg_sound_set(SOUND_BALANCE, global_settings.balance);
166 mpeg_sound_set(SOUND_VOLUME, global_settings.volume);
167 mpeg_sound_set(SOUND_LOUDNESS, global_settings.loudness);
168 mpeg_sound_set(SOUND_SUPERBASS, global_settings.bass_boost);
169 mpeg_sound_set(SOUND_AVC, global_settings.avc);
171 status_set_playmode(STATUS_STOP);
173 /* Yes, we use the D/A for monitoring */
174 peak_meter_playback(true);
176 peak_meter_enabled = true;
178 if (global_settings.rec_prerecord_time)
179 talk_buffer_steal(); /* will use the mp3 buffer */
181 mpeg_set_recording_options(global_settings.rec_frequency,
182 global_settings.rec_quality,
183 1, /* Line In */
184 global_settings.rec_channels,
185 global_settings.rec_editable,
186 global_settings.rec_prerecord_time);
189 mpeg_set_recording_gain(mpeg_sound_default(SOUND_LEFT_GAIN),
190 mpeg_sound_default(SOUND_RIGHT_GAIN), false);
192 fmradio_set(2, 0x140884); /* 5kHz, 7.2MHz crystal */
193 radio_set_frequency(curr_freq);
194 curr_preset = find_preset(curr_freq);
196 buttonbar_set(str(LANG_BUTTONBAR_MENU), str(LANG_FM_BUTTONBAR_PRESETS),
197 str(LANG_FM_BUTTONBAR_RECORD));
199 while(!done)
201 if(search_dir)
203 curr_freq += search_dir * FREQ_STEP;
204 if(curr_freq < MIN_FREQ)
205 curr_freq = MAX_FREQ;
206 if(curr_freq > MAX_FREQ)
207 curr_freq = MIN_FREQ;
209 /* Tune in and delay */
210 radio_set_frequency(curr_freq);
211 sleep(1);
213 /* Start IF measurement */
214 fmradio_set(1, 0x100006 | pll_cnt << 3);
215 sleep(1);
217 /* Now check how close to the IF frequency we are */
218 val = fmradio_read(3);
219 i_freq = (val & 0x7ffff) / 80;
221 /* Stop searching if the IF frequency is close to 10.7MHz */
222 if(i_freq > 1065 && i_freq < 1075)
224 search_dir = 0;
225 curr_preset = find_preset(curr_freq);
228 update_screen = true;
231 if(search_dir)
232 button = button_get(false);
233 else
234 button = button_get_w_tmo(HZ / peak_meter_fps);
235 switch(button)
237 case BUTTON_OFF:
238 if(mpeg_status() == MPEG_STATUS_RECORD)
240 mpeg_stop();
241 status_set_playmode(STATUS_STOP);
243 else
245 radio_stop();
246 done = true;
248 update_screen = true;
249 break;
251 case BUTTON_F3:
252 if(mpeg_status() == MPEG_STATUS_RECORD)
254 mpeg_new_file(rec_create_filename(buf));
255 update_screen = true;
257 else
259 have_recorded = true;
260 talk_buffer_steal(); /* we use the mp3 buffer */
261 mpeg_record(rec_create_filename(buf));
262 status_set_playmode(STATUS_RECORD);
263 update_screen = true;
265 last_seconds = 0;
266 break;
268 case BUTTON_ON | BUTTON_REL:
269 done = true;
270 keep_playing = true;
271 break;
273 case BUTTON_LEFT:
274 curr_freq -= FREQ_STEP;
275 if(curr_freq < MIN_FREQ)
276 curr_freq = MIN_FREQ;
278 radio_set_frequency(curr_freq);
279 curr_preset = find_preset(curr_freq);
280 search_dir = 0;
281 update_screen = true;
282 break;
284 case BUTTON_RIGHT:
285 curr_freq += FREQ_STEP;
286 if(curr_freq > MAX_FREQ)
287 curr_freq = MAX_FREQ;
289 radio_set_frequency(curr_freq);
290 curr_preset = find_preset(curr_freq);
291 search_dir = 0;
292 update_screen = true;
293 break;
295 case BUTTON_LEFT | BUTTON_REPEAT:
296 search_dir = -1;
297 break;
299 case BUTTON_RIGHT | BUTTON_REPEAT:
300 search_dir = 1;
301 break;
303 case BUTTON_UP:
304 case BUTTON_UP | BUTTON_REPEAT:
305 global_settings.volume++;
306 if(global_settings.volume > mpeg_sound_max(SOUND_VOLUME))
307 global_settings.volume = mpeg_sound_max(SOUND_VOLUME);
308 mpeg_sound_set(SOUND_VOLUME, global_settings.volume);
309 update_screen = true;
310 settings_save();
311 break;
313 case BUTTON_DOWN:
314 case BUTTON_DOWN | BUTTON_REPEAT:
315 global_settings.volume--;
316 if(global_settings.volume < mpeg_sound_min(SOUND_VOLUME))
317 global_settings.volume = mpeg_sound_min(SOUND_VOLUME);
318 mpeg_sound_set(SOUND_VOLUME, global_settings.volume);
319 update_screen = true;
320 settings_save();
321 break;
323 case BUTTON_F1:
324 radio_menu();
325 curr_preset = find_preset(curr_freq);
326 lcd_clear_display();
327 lcd_setmargins(0, 8);
328 buttonbar_set(str(LANG_BUTTONBAR_MENU),
329 str(LANG_FM_BUTTONBAR_PRESETS),
330 str(LANG_FM_BUTTONBAR_RECORD));
331 update_screen = true;
332 break;
334 case BUTTON_F2:
335 radio_preset_select();
336 curr_preset = find_preset(curr_freq);
337 lcd_clear_display();
338 lcd_setmargins(0, 8);
339 buttonbar_set(str(LANG_BUTTONBAR_MENU),
340 str(LANG_FM_BUTTONBAR_PRESETS),
341 str(LANG_FM_BUTTONBAR_RECORD));
342 update_screen = true;
343 break;
345 case BUTTON_PLAY:
346 if(!screen_freeze)
348 splash(0, true, "Screen frozen");
349 lcd_update();
350 screen_freeze = true;
352 else
354 update_screen = true;
355 screen_freeze = false;
357 break;
359 case SYS_USB_CONNECTED:
360 /* Only accept USB connection when not recording */
361 if(mpeg_status() != MPEG_STATUS_RECORD)
363 usb_screen();
364 fmradio_set_status(0);
365 screen_freeze = true; /* Cosmetic: makes sure the
366 radio screen doesn't redraw */
367 done = true;
369 break;
372 peak_meter_peek();
374 if(!screen_freeze)
376 lcd_setmargins(0, 8);
378 /* Only display the peak meter when not recording */
379 if(!mpeg_status())
381 lcd_clearrect(0, 8 + fh*(top_of_screen + 3), LCD_WIDTH, fh);
382 peak_meter_draw(0, 8 + fh*(top_of_screen + 3), LCD_WIDTH, fh);
383 lcd_update_rect(0, 8 + fh*(top_of_screen + 3), LCD_WIDTH, fh);
386 if(TIME_AFTER(current_tick, timeout))
388 timeout = current_tick + HZ;
390 val = fmradio_read(3);
391 stereo = (val & 0x100000)?true:false;
392 if(stereo != last_stereo_status)
394 update_screen = true;
395 last_stereo_status = stereo;
399 seconds = mpeg_recorded_time() / HZ;
401 if(update_screen || seconds > last_seconds)
403 last_seconds = seconds;
405 lcd_setfont(FONT_UI);
407 if(curr_preset >= 0)
409 lcd_puts_scroll(0, top_of_screen,
410 presets[curr_preset].name);
412 else
414 lcd_clearrect(0, 8 + top_of_screen*fh, LCD_WIDTH, fh);
417 freq = curr_freq / 100000;
418 snprintf(buf, 128, str(LANG_FM_STATION), freq / 10, freq % 10);
419 lcd_puts(0, top_of_screen + 1, buf);
421 snprintf(buf, 128,
422 stereo?str(LANG_CHANNEL_STEREO):
423 str(LANG_CHANNEL_MONO));
424 lcd_puts(0, top_of_screen + 2, buf);
426 if(mpeg_status() == MPEG_STATUS_RECORD)
428 hours = seconds / 3600;
429 minutes = (seconds - (hours * 3600)) / 60;
430 snprintf(buf, 32, "%s %02d:%02d:%02d",
431 str(LANG_RECORDING_TIME),
432 hours, minutes, seconds%60);
433 lcd_puts(0, top_of_screen + 3, buf);
435 else
437 snprintf(buf, 32, "%s %02d",
438 str(LANG_RECORD_PRERECORD), seconds%60);
439 lcd_puts(0, top_of_screen + 3, buf);
442 /* Only force the redraw if update_screen is true */
443 status_draw(update_screen);
445 buttonbar_draw();
447 lcd_update();
449 update_screen = false;
453 if(mpeg_status() & MPEG_STATUS_ERROR)
455 done = true;
460 if(mpeg_status() & MPEG_STATUS_ERROR)
462 status_set_playmode(STATUS_STOP);
463 splash(0, true, str(LANG_DISK_FULL));
464 status_draw(true);
465 lcd_update();
466 mpeg_error_clear();
468 while(1)
470 button = button_get(true);
471 if(button == (BUTTON_OFF | BUTTON_REL))
472 break;
476 mpeg_init_playback();
478 mpeg_sound_channel_config(global_settings.channel_config);
479 mpeg_sound_set(SOUND_BASS, global_settings.bass);
480 mpeg_sound_set(SOUND_TREBLE, global_settings.treble);
481 mpeg_sound_set(SOUND_BALANCE, global_settings.balance);
482 mpeg_sound_set(SOUND_VOLUME, global_settings.volume);
483 mpeg_sound_set(SOUND_LOUDNESS, global_settings.loudness);
484 mpeg_sound_set(SOUND_SUPERBASS, global_settings.bass_boost);
485 mpeg_sound_set(SOUND_AVC, global_settings.avc);
487 fmradio_set_status(0);
489 if(keep_playing)
491 /* Enable the Left and right A/D Converter */
492 mpeg_set_recording_gain(mpeg_sound_default(SOUND_LEFT_GAIN),
493 mpeg_sound_default(SOUND_RIGHT_GAIN), false);
494 mas_codec_writereg(6, 0x4000);
496 return have_recorded;
499 static bool parseline(char* line, char** freq, char** name)
501 char* ptr;
503 while ( isspace(*line) )
504 line++;
506 if ( *line == '#' )
507 return false;
509 ptr = strchr(line, ':');
510 if ( !ptr )
511 return false;
513 *freq = line;
514 *ptr = 0;
515 ptr++;
516 while (isspace(*ptr))
517 ptr++;
518 *name = ptr;
519 return true;
522 void radio_save_presets(void)
524 int fd;
525 int i;
527 fd = creat(default_filename, O_WRONLY);
528 if(fd >= 0)
530 for(i = 0;i < MAX_PRESETS;i++)
532 fprintf(fd, "%d:%s\n", presets[i].frequency, presets[i].name);
534 close(fd);
536 else
538 splash(HZ*2, true, str(LANG_FM_PRESET_SAVE_FAILED));
542 void radio_load_presets(void)
544 int fd;
545 int rc;
546 char buf[128];
547 char *freq;
548 char *name;
549 bool done = false;
550 int i;
552 if(!presets_loaded)
554 memset(presets, 0, sizeof(presets));
556 fd = open(default_filename, O_RDONLY);
557 if(fd >= 0)
559 i = 0;
560 while(!done && i < MAX_PRESETS)
562 rc = read_line(fd, buf, 128);
563 if(rc > 0)
565 if(parseline(buf, &freq, &name))
567 presets[i].frequency = atoi(freq);
568 strncpy(presets[i].name, name, 27);
569 presets[i].name[27] = 0;
570 i++;
573 else
574 done = true;
576 close(fd);
579 presets_loaded = true;
582 bool radio_preset_select(void)
584 struct menu_item menu[MAX_PRESETS];
585 int m, result;
586 int i;
587 bool reload_dir = false;
588 int num_presets;
590 if(presets_loaded)
592 num_presets = 0;
594 for(i = 0;i < MAX_PRESETS;i++)
596 if(presets[i].frequency)
598 menu[num_presets].desc = presets[i].name;
599 menu[num_presets].voice_id = -1;
600 /* We use the function pointer entry for the preset
601 entry index */
602 menu[num_presets++].function = (void *)i;
606 if(num_presets)
608 /* DIY menu handling, since we want to exit after selection */
609 m = menu_init( menu, num_presets, NULL, NULL, NULL, NULL );
610 result = menu_show(m);
611 menu_exit(m);
612 if (result == MENU_SELECTED_EXIT)
613 return false;
614 else if (result == MENU_ATTACHED_USB)
615 reload_dir = true;
617 if (result >= 0)
619 i = (int)menu[result].function;
620 curr_freq = presets[i].frequency;
621 radio_set_frequency(curr_freq);
624 else
626 splash(HZ*2, true, str(LANG_FM_NO_PRESETS));
630 return reload_dir;
633 static bool radio_add_preset(void)
635 char buf[27];
636 int i = find_preset(0);
638 if(i >= 0)
640 memset(buf, 0, 27);
642 if (!kbd_input(buf, 27))
644 buf[27] = 0;
645 strcpy(presets[i].name, buf);
646 presets[i].frequency = curr_freq;
647 radio_save_presets();
650 else
652 splash(HZ*2, true, str(LANG_FM_NO_FREE_PRESETS));
654 return true;
657 bool radio_delete_preset(void)
659 struct menu_item menu[MAX_PRESETS];
660 int m, result;
661 int i;
662 bool reload_dir = false;
663 int num_presets;
665 if(presets_loaded)
667 num_presets = 0;
669 for(i = 0;i < MAX_PRESETS;i++)
671 if(presets[i].frequency)
673 menu[num_presets].desc = presets[i].name;
674 menu[num_presets].voice_id = -1;
675 /* We use the function pointer entry for the preset
676 entry index */
677 menu[num_presets++].function = (void *)i;
681 /* DIY menu handling, since we want to exit after selection */
682 m = menu_init( menu, num_presets, NULL, NULL, NULL, NULL );
683 result = menu_show(m);
684 menu_exit(m);
685 if (result == MENU_SELECTED_EXIT)
686 return false;
687 else if (result == MENU_ATTACHED_USB)
688 reload_dir = true;
690 if (result >= 0)
692 i = (int)menu[result].function;
693 presets[i].frequency = 0;
694 radio_save_presets();
698 return reload_dir;
701 static bool fm_recording_settings(void)
703 bool ret;
705 ret = recording_menu(true);
706 if(!ret)
708 if (global_settings.rec_prerecord_time)
709 talk_buffer_steal(); /* will use the mp3 buffer */
711 mpeg_set_recording_options(global_settings.rec_frequency,
712 global_settings.rec_quality,
713 1, /* Line In */
714 global_settings.rec_channels,
715 global_settings.rec_editable,
716 global_settings.rec_prerecord_time);
718 return ret;
721 bool radio_menu(void)
723 struct menu_item radio_menu_items[] = {
724 { STR(LANG_FM_SAVE_PRESET), radio_add_preset },
725 { STR(LANG_FM_DELETE_PRESET), radio_delete_preset },
726 { STR(LANG_SOUND_SETTINGS), sound_menu },
727 { STR(LANG_RECORDING_SETTINGS), fm_recording_settings }
729 int m;
730 bool result;
732 m = menu_init( radio_menu_items,
733 sizeof radio_menu_items / sizeof(struct menu_item), NULL,
734 NULL, NULL, NULL);
735 result = menu_run(m);
736 menu_exit(m);
737 return result;
740 #endif