diff options
-rw-r--r-- | apps/lang/english.lang | 10 | ||||
-rw-r--r-- | apps/recorder/radio.c | 47 | ||||
-rw-r--r-- | apps/recorder/recording.c | 75 | ||||
-rw-r--r-- | apps/settings.c | 30 | ||||
-rw-r--r-- | apps/settings.h | 2 | ||||
-rw-r--r-- | apps/sound_menu.c | 18 | ||||
-rw-r--r-- | firmware/export/mpeg.h | 7 | ||||
-rw-r--r-- | firmware/mpeg.c | 582 |
8 files changed, 526 insertions, 245 deletions
diff --git a/apps/lang/english.lang b/apps/lang/english.lang index ffdc12b306..5cad562a7f 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang | |||
@@ -1847,3 +1847,13 @@ id: LANG_FLIP_DISPLAY | |||
1847 | desc: in settings_menu, option to turn display+buttos by 180 degreed | 1847 | desc: in settings_menu, option to turn display+buttos by 180 degreed |
1848 | eng: "Upside Down" | 1848 | eng: "Upside Down" |
1849 | new: | 1849 | new: |
1850 | |||
1851 | id: LANG_RECORD_PRERECORD | ||
1852 | desc: in recording and radio screen | ||
1853 | eng: "Prerecording" | ||
1854 | new: | ||
1855 | |||
1856 | id: LANG_RECORD_PRERECORD_TIME | ||
1857 | desc: in recording settings_menu | ||
1858 | eng: "Prerecord time" | ||
1859 | new: | ||
diff --git a/apps/recorder/radio.c b/apps/recorder/radio.c index ab0c1eb004..519e919964 100644 --- a/apps/recorder/radio.c +++ b/apps/recorder/radio.c | |||
@@ -172,9 +172,10 @@ bool radio_screen(void) | |||
172 | 172 | ||
173 | mpeg_set_recording_options(global_settings.rec_frequency, | 173 | mpeg_set_recording_options(global_settings.rec_frequency, |
174 | global_settings.rec_quality, | 174 | global_settings.rec_quality, |
175 | 1 /* Line In */, | 175 | 1, /* Line In */ |
176 | global_settings.rec_channels, | 176 | global_settings.rec_channels, |
177 | global_settings.rec_editable); | 177 | global_settings.rec_editable, |
178 | global_settings.rec_prerecord_time); | ||
178 | 179 | ||
179 | 180 | ||
180 | mpeg_set_recording_gain(mpeg_sound_default(SOUND_LEFT_GAIN), | 181 | mpeg_set_recording_gain(mpeg_sound_default(SOUND_LEFT_GAIN), |
@@ -230,7 +231,7 @@ bool radio_screen(void) | |||
230 | switch(button) | 231 | switch(button) |
231 | { | 232 | { |
232 | case BUTTON_OFF: | 233 | case BUTTON_OFF: |
233 | if(mpeg_status()) | 234 | if(mpeg_status() == MPEG_STATUS_RECORD) |
234 | { | 235 | { |
235 | mpeg_stop(); | 236 | mpeg_stop(); |
236 | status_set_playmode(STATUS_STOP); | 237 | status_set_playmode(STATUS_STOP); |
@@ -244,17 +245,16 @@ bool radio_screen(void) | |||
244 | break; | 245 | break; |
245 | 246 | ||
246 | case BUTTON_F3: | 247 | case BUTTON_F3: |
247 | /* Only act if the mpeg is stopped */ | 248 | if(mpeg_status() == MPEG_STATUS_RECORD) |
248 | if(!mpeg_status()) | ||
249 | { | 249 | { |
250 | have_recorded = true; | 250 | mpeg_new_file(rec_create_filename()); |
251 | mpeg_record(rec_create_filename()); | ||
252 | status_set_playmode(STATUS_RECORD); | ||
253 | update_screen = true; | 251 | update_screen = true; |
254 | } | 252 | } |
255 | else | 253 | else |
256 | { | 254 | { |
257 | mpeg_new_file(rec_create_filename()); | 255 | have_recorded = true; |
256 | mpeg_record(rec_create_filename()); | ||
257 | status_set_playmode(STATUS_RECORD); | ||
258 | update_screen = true; | 258 | update_screen = true; |
259 | } | 259 | } |
260 | last_seconds = 0; | 260 | last_seconds = 0; |
@@ -353,11 +353,12 @@ bool radio_screen(void) | |||
353 | 353 | ||
354 | case SYS_USB_CONNECTED: | 354 | case SYS_USB_CONNECTED: |
355 | /* Only accept USB connection when not recording */ | 355 | /* Only accept USB connection when not recording */ |
356 | if(!mpeg_status()) | 356 | if(mpeg_status() != MPEG_STATUS_RECORD) |
357 | { | 357 | { |
358 | usb_screen(); | 358 | usb_screen(); |
359 | fmradio_set_status(0); | 359 | fmradio_set_status(0); |
360 | have_recorded = true; /* Refreshes the browser later on */ | 360 | screen_freeze = true; /* Cosmetic: makes sure the |
361 | radio screen doesn't redraw */ | ||
361 | done = true; | 362 | done = true; |
362 | } | 363 | } |
363 | break; | 364 | break; |
@@ -368,6 +369,8 @@ bool radio_screen(void) | |||
368 | if(!screen_freeze) | 369 | if(!screen_freeze) |
369 | { | 370 | { |
370 | lcd_setmargins(0, 8); | 371 | lcd_setmargins(0, 8); |
372 | |||
373 | /* Only display the peak meter when not recording */ | ||
371 | if(!mpeg_status()) | 374 | if(!mpeg_status()) |
372 | { | 375 | { |
373 | lcd_clearrect(0, 8 + fh*(top_of_screen + 3), LCD_WIDTH, fh); | 376 | lcd_clearrect(0, 8 + fh*(top_of_screen + 3), LCD_WIDTH, fh); |
@@ -415,7 +418,7 @@ bool radio_screen(void) | |||
415 | str(LANG_CHANNEL_MONO)); | 418 | str(LANG_CHANNEL_MONO)); |
416 | lcd_puts(0, top_of_screen + 2, buf); | 419 | lcd_puts(0, top_of_screen + 2, buf); |
417 | 420 | ||
418 | if(mpeg_status()) | 421 | if(mpeg_status() == MPEG_STATUS_RECORD) |
419 | { | 422 | { |
420 | hours = seconds / 3600; | 423 | hours = seconds / 3600; |
421 | minutes = (seconds - (hours * 3600)) / 60; | 424 | minutes = (seconds - (hours * 3600)) / 60; |
@@ -424,6 +427,12 @@ bool radio_screen(void) | |||
424 | hours, minutes, seconds%60); | 427 | hours, minutes, seconds%60); |
425 | lcd_puts(0, top_of_screen + 3, buf); | 428 | lcd_puts(0, top_of_screen + 3, buf); |
426 | } | 429 | } |
430 | else | ||
431 | { | ||
432 | snprintf(buf, 32, "%s %02d", | ||
433 | str(LANG_RECORD_PRERECORD), seconds%60); | ||
434 | lcd_puts(0, top_of_screen + 3, buf); | ||
435 | } | ||
427 | 436 | ||
428 | /* Only force the redraw if update_screen is true */ | 437 | /* Only force the redraw if update_screen is true */ |
429 | status_draw(update_screen); | 438 | status_draw(update_screen); |
@@ -687,7 +696,19 @@ bool radio_delete_preset(void) | |||
687 | 696 | ||
688 | static bool fm_recording_settings(void) | 697 | static bool fm_recording_settings(void) |
689 | { | 698 | { |
690 | return recording_menu(true); | 699 | bool ret; |
700 | |||
701 | ret = recording_menu(true); | ||
702 | if(!ret) | ||
703 | { | ||
704 | mpeg_set_recording_options(global_settings.rec_frequency, | ||
705 | global_settings.rec_quality, | ||
706 | 1, /* Line In */ | ||
707 | global_settings.rec_channels, | ||
708 | global_settings.rec_editable, | ||
709 | global_settings.rec_prerecord_time); | ||
710 | } | ||
711 | return ret; | ||
691 | } | 712 | } |
692 | 713 | ||
693 | bool radio_menu(void) | 714 | bool radio_menu(void) |
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c index d94508e547..bc26ff8495 100644 --- a/apps/recorder/recording.c +++ b/apps/recorder/recording.c | |||
@@ -160,7 +160,8 @@ bool recording_screen(void) | |||
160 | global_settings.rec_quality, | 160 | global_settings.rec_quality, |
161 | global_settings.rec_source, | 161 | global_settings.rec_source, |
162 | global_settings.rec_channels, | 162 | global_settings.rec_channels, |
163 | global_settings.rec_editable); | 163 | global_settings.rec_editable, |
164 | global_settings.rec_prerecord_time); | ||
164 | 165 | ||
165 | set_gain(); | 166 | set_gain(); |
166 | 167 | ||
@@ -174,7 +175,7 @@ bool recording_screen(void) | |||
174 | switch(button) | 175 | switch(button) |
175 | { | 176 | { |
176 | case BUTTON_OFF: | 177 | case BUTTON_OFF: |
177 | if(mpeg_status()) | 178 | if(mpeg_status() & MPEG_STATUS_RECORD) |
178 | { | 179 | { |
179 | mpeg_stop(); | 180 | mpeg_stop(); |
180 | status_set_playmode(STATUS_STOP); | 181 | status_set_playmode(STATUS_STOP); |
@@ -190,7 +191,7 @@ bool recording_screen(void) | |||
190 | 191 | ||
191 | case BUTTON_PLAY: | 192 | case BUTTON_PLAY: |
192 | /* Only act if the mpeg is stopped */ | 193 | /* Only act if the mpeg is stopped */ |
193 | if(!mpeg_status()) | 194 | if(!(mpeg_status() & MPEG_STATUS_RECORD)) |
194 | { | 195 | { |
195 | have_recorded = true; | 196 | have_recorded = true; |
196 | mpeg_record(rec_create_filename()); | 197 | mpeg_record(rec_create_filename()); |
@@ -305,13 +306,16 @@ bool recording_screen(void) | |||
305 | if (recording_menu(false)) | 306 | if (recording_menu(false)) |
306 | return SYS_USB_CONNECTED; | 307 | return SYS_USB_CONNECTED; |
307 | settings_save(); | 308 | settings_save(); |
309 | |||
308 | mpeg_set_recording_options(global_settings.rec_frequency, | 310 | mpeg_set_recording_options(global_settings.rec_frequency, |
309 | global_settings.rec_quality, | 311 | global_settings.rec_quality, |
310 | global_settings.rec_source, | 312 | global_settings.rec_source, |
311 | global_settings.rec_channels, | 313 | global_settings.rec_channels, |
312 | global_settings.rec_editable); | 314 | global_settings.rec_editable, |
315 | global_settings.rec_prerecord_time); | ||
313 | 316 | ||
314 | set_gain(); | 317 | set_gain(); |
318 | |||
315 | update_countdown = 1; /* Update immediately */ | 319 | update_countdown = 1; /* Update immediately */ |
316 | 320 | ||
317 | lcd_setfont(FONT_SYSFIXED); | 321 | lcd_setfont(FONT_SYSFIXED); |
@@ -319,7 +323,7 @@ bool recording_screen(void) | |||
319 | break; | 323 | break; |
320 | 324 | ||
321 | case BUTTON_F2: | 325 | case BUTTON_F2: |
322 | if(!mpeg_status()) | 326 | if(mpeg_status() != MPEG_STATUS_RECORD) |
323 | { | 327 | { |
324 | if (f2_rec_screen()) | 328 | if (f2_rec_screen()) |
325 | { | 329 | { |
@@ -332,7 +336,7 @@ bool recording_screen(void) | |||
332 | break; | 336 | break; |
333 | 337 | ||
334 | case BUTTON_F3: | 338 | case BUTTON_F3: |
335 | if(!mpeg_status()) | 339 | if(mpeg_status() != MPEG_STATUS_RECORD) |
336 | { | 340 | { |
337 | if (f3_rec_screen()) | 341 | if (f3_rec_screen()) |
338 | { | 342 | { |
@@ -346,14 +350,12 @@ bool recording_screen(void) | |||
346 | 350 | ||
347 | case SYS_USB_CONNECTED: | 351 | case SYS_USB_CONNECTED: |
348 | /* Only accept USB connection when not recording */ | 352 | /* Only accept USB connection when not recording */ |
349 | if(!mpeg_status()) | 353 | if(mpeg_status() != MPEG_STATUS_RECORD) |
350 | { | 354 | { |
351 | usb_screen(); | 355 | usb_screen(); |
352 | have_recorded = true; /* Refreshes the browser later on */ | 356 | have_recorded = true; /* Refreshes the browser later on */ |
353 | done = true; | 357 | done = true; |
354 | } | 358 | } |
355 | lcd_setfont(FONT_SYSFIXED); | ||
356 | lcd_setmargins(global_settings.invert_cursor ? 0 : w, 8); | ||
357 | break; | 359 | break; |
358 | } | 360 | } |
359 | 361 | ||
@@ -387,21 +389,28 @@ bool recording_screen(void) | |||
387 | 389 | ||
388 | dseconds = rec_timesplit_seconds(); | 390 | dseconds = rec_timesplit_seconds(); |
389 | 391 | ||
390 | /* Display the split interval if the record timesplit | 392 | if(mpeg_status() & MPEG_STATUS_PRERECORD) |
391 | is active */ | ||
392 | if (global_settings.rec_timesplit) | ||
393 | { | 393 | { |
394 | /* Display the record timesplit interval rather than | 394 | snprintf(buf, 32, "%s...", str(LANG_RECORD_PRERECORD)); |
395 | the file size if the record timer is active */ | ||
396 | dhours = dseconds / 3600; | ||
397 | dminutes = (dseconds - (dhours * 3600)) / 60; | ||
398 | snprintf(buf, 32, "%s %02d:%02d", | ||
399 | str(LANG_RECORD_TIMESPLIT_REC), | ||
400 | dhours, dminutes); | ||
401 | } | 395 | } |
402 | else | 396 | else |
403 | snprintf(buf, 32, "%s %s", str(LANG_RECORDING_SIZE), | 397 | { |
404 | num2max5(mpeg_num_recorded_bytes(), buf2)); | 398 | /* Display the split interval if the record timesplit |
399 | is active */ | ||
400 | if (global_settings.rec_timesplit) | ||
401 | { | ||
402 | /* Display the record timesplit interval rather than | ||
403 | the file size if the record timer is active */ | ||
404 | dhours = dseconds / 3600; | ||
405 | dminutes = (dseconds - (dhours * 3600)) / 60; | ||
406 | snprintf(buf, 32, "%s %02d:%02d", | ||
407 | str(LANG_RECORD_TIMESPLIT_REC), | ||
408 | dhours, dminutes); | ||
409 | } | ||
410 | else | ||
411 | snprintf(buf, 32, "%s %s", str(LANG_RECORDING_SIZE), | ||
412 | num2max5(mpeg_num_recorded_bytes(), buf2)); | ||
413 | } | ||
405 | lcd_puts(0, 1, buf); | 414 | lcd_puts(0, 1, buf); |
406 | 415 | ||
407 | /* We will do file splitting regardless, since the OFF | 416 | /* We will do file splitting regardless, since the OFF |
@@ -622,7 +631,8 @@ bool f2_rec_screen(void) | |||
622 | global_settings.rec_quality, | 631 | global_settings.rec_quality, |
623 | global_settings.rec_source, | 632 | global_settings.rec_source, |
624 | global_settings.rec_channels, | 633 | global_settings.rec_channels, |
625 | global_settings.rec_editable); | 634 | global_settings.rec_editable, |
635 | global_settings.rec_prerecord_time); | ||
626 | 636 | ||
627 | set_gain(); | 637 | set_gain(); |
628 | 638 | ||
@@ -671,22 +681,6 @@ bool f3_rec_screen(void) | |||
671 | used = true; | 681 | used = true; |
672 | break; | 682 | break; |
673 | 683 | ||
674 | case BUTTON_DOWN: | ||
675 | case BUTTON_F3 | BUTTON_DOWN: | ||
676 | global_settings.rec_frequency++; | ||
677 | if(global_settings.rec_frequency > 5) | ||
678 | global_settings.rec_frequency = 0; | ||
679 | used = true; | ||
680 | break; | ||
681 | |||
682 | case BUTTON_RIGHT: | ||
683 | case BUTTON_F3 | BUTTON_RIGHT: | ||
684 | global_settings.rec_channels++; | ||
685 | if(global_settings.rec_channels > 1) | ||
686 | global_settings.rec_channels = 0; | ||
687 | used = true; | ||
688 | break; | ||
689 | |||
690 | case BUTTON_F3 | BUTTON_REL: | 684 | case BUTTON_F3 | BUTTON_REL: |
691 | if ( used ) | 685 | if ( used ) |
692 | exit = true; | 686 | exit = true; |
@@ -707,10 +701,11 @@ bool f3_rec_screen(void) | |||
707 | global_settings.rec_quality, | 701 | global_settings.rec_quality, |
708 | global_settings.rec_source, | 702 | global_settings.rec_source, |
709 | global_settings.rec_channels, | 703 | global_settings.rec_channels, |
710 | global_settings.rec_editable); | 704 | global_settings.rec_editable, |
705 | global_settings.rec_prerecord_time); | ||
711 | 706 | ||
712 | set_gain(); | 707 | set_gain(); |
713 | 708 | ||
714 | settings_save(); | 709 | settings_save(); |
715 | lcd_setfont(FONT_UI); | 710 | lcd_setfont(FONT_UI); |
716 | 711 | ||
diff --git a/apps/settings.c b/apps/settings.c index b2fa22170b..35e75d12ca 100644 --- a/apps/settings.c +++ b/apps/settings.c | |||
@@ -129,6 +129,9 @@ location used, and reset the setting in question with a factory default if | |||
129 | needed. Memory locations not used by a given version should not be | 129 | needed. Memory locations not used by a given version should not be |
130 | modified unless the header & checksum test fails. | 130 | modified unless the header & checksum test fails. |
131 | 131 | ||
132 | Because 0xff mean that the byte is unused, care must be taken so that | ||
133 | a used byte can't have the value 0xff. Either use only 7 bits, or make sure | ||
134 | that the value will never be 0xff. | ||
132 | 135 | ||
133 | Rest of config block, only saved to disk: | 136 | Rest of config block, only saved to disk: |
134 | 0xA8 (char)jump scroll mode (only for player) | 137 | 0xA8 (char)jump scroll mode (only for player) |
@@ -151,9 +154,8 @@ Rest of config block, only saved to disk: | |||
151 | 0xB8 (char[20]) WPS file | 154 | 0xB8 (char[20]) WPS file |
152 | 0xCC (char[20]) Lang file | 155 | 0xCC (char[20]) Lang file |
153 | 0xE0 (char[20]) Font file | 156 | 0xE0 (char[20]) Font file |
154 | 0xF4 <unused> | 157 | 0xF4 Prerecording time (bit 0-4) |
155 | 0xF8 <unused> | 158 | 0xF5-0xFF <unused> |
156 | 0xFC <unused> | ||
157 | 159 | ||
158 | *************************************/ | 160 | *************************************/ |
159 | 161 | ||
@@ -432,6 +434,8 @@ int settings_save( void ) | |||
432 | strncpy(&config_block[0xcc], global_settings.lang_file, MAX_FILENAME); | 434 | strncpy(&config_block[0xcc], global_settings.lang_file, MAX_FILENAME); |
433 | strncpy(&config_block[0xe0], global_settings.font_file, MAX_FILENAME); | 435 | strncpy(&config_block[0xe0], global_settings.font_file, MAX_FILENAME); |
434 | 436 | ||
437 | config_block[0xf4]=(unsigned char)global_settings.rec_prerecord_time; | ||
438 | |||
435 | if(save_config_buffer()) | 439 | if(save_config_buffer()) |
436 | { | 440 | { |
437 | lcd_clear_display(); | 441 | lcd_clear_display(); |
@@ -756,6 +760,10 @@ void settings_load(void) | |||
756 | strncpy(global_settings.wps_file, &config_block[0xb8], MAX_FILENAME); | 760 | strncpy(global_settings.wps_file, &config_block[0xb8], MAX_FILENAME); |
757 | strncpy(global_settings.lang_file, &config_block[0xcc], MAX_FILENAME); | 761 | strncpy(global_settings.lang_file, &config_block[0xcc], MAX_FILENAME); |
758 | strncpy(global_settings.font_file, &config_block[0xe0], MAX_FILENAME); | 762 | strncpy(global_settings.font_file, &config_block[0xe0], MAX_FILENAME); |
763 | |||
764 | if (config_block[0xf4] != 0xff) | ||
765 | global_settings.rec_prerecord_time = config_block[0xf4]; | ||
766 | |||
759 | #ifdef HAVE_LCD_CHARCELLS | 767 | #ifdef HAVE_LCD_CHARCELLS |
760 | if (config_block[0xa8] != 0xff) | 768 | if (config_block[0xa8] != 0xff) |
761 | global_settings.jump_scroll = config_block[0xa8]; | 769 | global_settings.jump_scroll = config_block[0xa8]; |
@@ -1079,6 +1087,9 @@ bool settings_load_config(char* file) | |||
1079 | else if (!strcasecmp(name, "editable recordings")) { | 1087 | else if (!strcasecmp(name, "editable recordings")) { |
1080 | set_cfg_bool(&global_settings.rec_editable, value); | 1088 | set_cfg_bool(&global_settings.rec_editable, value); |
1081 | } | 1089 | } |
1090 | else if (!strcasecmp(name, "prerecording time")) { | ||
1091 | set_cfg_int(&global_settings.rec_prerecord_time, value, 0, 30); | ||
1092 | } | ||
1082 | #endif | 1093 | #endif |
1083 | else if (!strcasecmp(name, "idle poweroff")) { | 1094 | else if (!strcasecmp(name, "idle poweroff")) { |
1084 | static char* options[] = {"off","1","2","3","4","5","6","7","8", | 1095 | static char* options[] = {"off","1","2","3","4","5","6","7","8", |
@@ -1379,6 +1390,10 @@ bool settings_save_config(void) | |||
1379 | fprintf(fd, "line in: %s\r\n", boolopt[global_settings.line_in]); | 1390 | fprintf(fd, "line in: %s\r\n", boolopt[global_settings.line_in]); |
1380 | #endif | 1391 | #endif |
1381 | 1392 | ||
1393 | fprintf(fd, "max files in dir: %d\r\n", global_settings.max_files_in_dir); | ||
1394 | fprintf(fd, "max files in playlist: %d\r\n", | ||
1395 | global_settings.max_files_in_playlist); | ||
1396 | |||
1382 | #ifdef HAVE_MAS3587F | 1397 | #ifdef HAVE_MAS3587F |
1383 | fprintf(fd, "#\r\n# Recording\r\n#\r\n"); | 1398 | fprintf(fd, "#\r\n# Recording\r\n#\r\n"); |
1384 | fprintf(fd, "rec quality: %d\r\n", global_settings.rec_quality); | 1399 | fprintf(fd, "rec quality: %d\r\n", global_settings.rec_quality); |
@@ -1409,12 +1424,12 @@ bool settings_save_config(void) | |||
1409 | fprintf(fd, "editable recordings: %s\r\n", | 1424 | fprintf(fd, "editable recordings: %s\r\n", |
1410 | boolopt[global_settings.rec_editable]); | 1425 | boolopt[global_settings.rec_editable]); |
1411 | 1426 | ||
1427 | fprintf(fd, "prerecording time: %d\r\n", | ||
1428 | global_settings.rec_prerecord_time); | ||
1429 | |||
1412 | #endif | 1430 | #endif |
1413 | 1431 | ||
1414 | fprintf(fd, "max files in dir: %d\r\n", global_settings.max_files_in_dir); | 1432 | fprintf(fd, "#\r\n# Playlists\r\n#\r\n"); |
1415 | fprintf(fd, "max files in playlist: %d\r\n", | ||
1416 | global_settings.max_files_in_playlist); | ||
1417 | |||
1418 | { | 1433 | { |
1419 | static char* options[] = {"off", "on", "ask"}; | 1434 | static char* options[] = {"off", "on", "ask"}; |
1420 | fprintf(fd, "recursive directory insert: %s\r\n", | 1435 | fprintf(fd, "recursive directory insert: %s\r\n", |
@@ -1454,6 +1469,7 @@ void settings_reset(void) { | |||
1454 | global_settings.rec_left_gain = 2; /* 0dB */ | 1469 | global_settings.rec_left_gain = 2; /* 0dB */ |
1455 | global_settings.rec_right_gain = 2; /* 0dB */ | 1470 | global_settings.rec_right_gain = 2; /* 0dB */ |
1456 | global_settings.rec_editable = false; | 1471 | global_settings.rec_editable = false; |
1472 | global_settings.rec_prerecord_time = 0; | ||
1457 | global_settings.resume = RESUME_ASK; | 1473 | global_settings.resume = RESUME_ASK; |
1458 | global_settings.contrast = lcd_default_contrast(); | 1474 | global_settings.contrast = lcd_default_contrast(); |
1459 | global_settings.invert = DEFAULT_INVERT_SETTING; | 1475 | global_settings.invert = DEFAULT_INVERT_SETTING; |
diff --git a/apps/settings.h b/apps/settings.h index 074be0690a..49327b2f80 100644 --- a/apps/settings.h +++ b/apps/settings.h | |||
@@ -85,6 +85,8 @@ struct user_settings | |||
85 | 5 = 01:00, 6 = 02:00, 7 = 04:00, 8 = 06:00 | 85 | 5 = 01:00, 6 = 02:00, 7 = 04:00, 8 = 06:00 |
86 | 9 = 08:00, 10= 10:00, 11= 12:00, 12= 18:00, | 86 | 9 = 08:00, 10= 10:00, 11= 12:00, 12= 18:00, |
87 | 13= 24:00 */ | 87 | 13= 24:00 */ |
88 | |||
89 | int rec_prerecord_time; /* In seconds, 0-30, 0 means OFF */ | ||
88 | 90 | ||
89 | /* device settings */ | 91 | /* device settings */ |
90 | 92 | ||
diff --git a/apps/sound_menu.c b/apps/sound_menu.c index c5e254794e..557013df8a 100644 --- a/apps/sound_menu.c +++ b/apps/sound_menu.c | |||
@@ -241,6 +241,20 @@ static bool rectimesplit(void) | |||
241 | names, 14, NULL ); | 241 | names, 14, NULL ); |
242 | } | 242 | } |
243 | 243 | ||
244 | static bool recprerecord(void) | ||
245 | { | ||
246 | char *names[] = { | ||
247 | str(LANG_OFF),"1s","2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s", | ||
248 | "10s", "11s", "12s", "13s", "14s", "15s", "16s", "17s", "18s", "19s", | ||
249 | "20s", "21s", "22s", "23s", "24s", "25s", "26s", "27s", "28s", "29s", | ||
250 | "30s" | ||
251 | }; | ||
252 | |||
253 | return set_option(str(LANG_RECORD_PRERECORD_TIME), | ||
254 | &global_settings.rec_prerecord_time, INT, | ||
255 | names, 31, NULL ); | ||
256 | } | ||
257 | |||
244 | #endif /* HAVE_MAS3587F */ | 258 | #endif /* HAVE_MAS3587F */ |
245 | 259 | ||
246 | static void set_chanconf(int val) | 260 | static void set_chanconf(int val) |
@@ -294,7 +308,7 @@ bool recording_menu(bool no_source) | |||
294 | { | 308 | { |
295 | int m; | 309 | int m; |
296 | int i = 0; | 310 | int i = 0; |
297 | struct menu_items menu[6]; | 311 | struct menu_items menu[7]; |
298 | bool result; | 312 | bool result; |
299 | 313 | ||
300 | menu[i].desc = str(LANG_RECORDING_QUALITY); | 314 | menu[i].desc = str(LANG_RECORDING_QUALITY); |
@@ -311,6 +325,8 @@ bool recording_menu(bool no_source) | |||
311 | menu[i++].function = receditable; | 325 | menu[i++].function = receditable; |
312 | menu[i].desc = str(LANG_RECORD_TIMESPLIT); | 326 | menu[i].desc = str(LANG_RECORD_TIMESPLIT); |
313 | menu[i++].function = rectimesplit; | 327 | menu[i++].function = rectimesplit; |
328 | menu[i].desc = str(LANG_RECORD_PRERECORD_TIME); | ||
329 | menu[i++].function = recprerecord; | ||
314 | 330 | ||
315 | m=menu_init( menu, i ); | 331 | m=menu_init( menu, i ); |
316 | result = menu_run(m); | 332 | result = menu_run(m); |
diff --git a/firmware/export/mpeg.h b/firmware/export/mpeg.h index 63eecf51db..bbace28ecd 100644 --- a/firmware/export/mpeg.h +++ b/firmware/export/mpeg.h | |||
@@ -32,6 +32,8 @@ | |||
32 | #define MPEG_PLAY_PENDING_THRESHOLD 0x10000 | 32 | #define MPEG_PLAY_PENDING_THRESHOLD 0x10000 |
33 | #define MPEG_PLAY_PENDING_SWAPSIZE 0x10000 | 33 | #define MPEG_PLAY_PENDING_SWAPSIZE 0x10000 |
34 | 34 | ||
35 | #define MPEG_MAX_PRERECORD_SECONDS 30 | ||
36 | |||
35 | /* For ID3 info and VBR header */ | 37 | /* For ID3 info and VBR header */ |
36 | #define MPEG_RESERVED_HEADER_SPACE (4096 + 1500) | 38 | #define MPEG_RESERVED_HEADER_SPACE (4096 + 1500) |
37 | 39 | ||
@@ -89,7 +91,7 @@ void mpeg_record(char *filename); | |||
89 | void mpeg_new_file(char *filename); | 91 | void mpeg_new_file(char *filename); |
90 | void mpeg_set_recording_options(int frequency, int quality, | 92 | void mpeg_set_recording_options(int frequency, int quality, |
91 | int source, int channel_mode, | 93 | int source, int channel_mode, |
92 | bool editable); | 94 | bool editable, int prerecord_time); |
93 | void mpeg_set_recording_gain(int left, int right, bool use_mic); | 95 | void mpeg_set_recording_gain(int left, int right, bool use_mic); |
94 | unsigned long mpeg_recorded_time(void); | 96 | unsigned long mpeg_recorded_time(void); |
95 | unsigned long mpeg_num_recorded_bytes(void); | 97 | unsigned long mpeg_num_recorded_bytes(void); |
@@ -123,7 +125,8 @@ void mpeg_error_clear(void); | |||
123 | #define MPEG_STATUS_PLAY 1 | 125 | #define MPEG_STATUS_PLAY 1 |
124 | #define MPEG_STATUS_PAUSE 2 | 126 | #define MPEG_STATUS_PAUSE 2 |
125 | #define MPEG_STATUS_RECORD 4 | 127 | #define MPEG_STATUS_RECORD 4 |
126 | #define MPEG_STATUS_ERROR 8 | 128 | #define MPEG_STATUS_PRERECORD 8 |
129 | #define MPEG_STATUS_ERROR 16 | ||
127 | 130 | ||
128 | #define MPEGERR_DISK_FULL 1 | 131 | #define MPEGERR_DISK_FULL 1 |
129 | 132 | ||
diff --git a/firmware/mpeg.c b/firmware/mpeg.c index 80e871597e..43da221800 100644 --- a/firmware/mpeg.c +++ b/firmware/mpeg.c | |||
@@ -44,6 +44,7 @@ extern void bitswap(unsigned char *data, int length); | |||
44 | #ifdef HAVE_MAS3587F | 44 | #ifdef HAVE_MAS3587F |
45 | static void init_recording(void); | 45 | static void init_recording(void); |
46 | static void init_playback(void); | 46 | static void init_playback(void); |
47 | static void start_prerecording(void); | ||
47 | static void start_recording(void); | 48 | static void start_recording(void); |
48 | static void stop_recording(void); | 49 | static void stop_recording(void); |
49 | static int get_unsaved_space(void); | 50 | static int get_unsaved_space(void); |
@@ -497,6 +498,17 @@ static char recording_filename[MAX_PATH]; | |||
497 | static int rec_frequency_index; /* For create_xing_header() calls */ | 498 | static int rec_frequency_index; /* For create_xing_header() calls */ |
498 | static int rec_version_index; /* For create_xing_header() calls */ | 499 | static int rec_version_index; /* For create_xing_header() calls */ |
499 | static bool disable_xing_header; /* When splitting files */ | 500 | static bool disable_xing_header; /* When splitting files */ |
501 | |||
502 | static bool prerecording; /* True if prerecording is enabled */ | ||
503 | static bool is_prerecording; /* True if we are prerecording */ | ||
504 | static int prerecord_buffer[MPEG_MAX_PRERECORD_SECONDS]; /* Array of buffer | ||
505 | indexes for each | ||
506 | prerecorded | ||
507 | second */ | ||
508 | static int prerecord_index; /* Current index in the prerecord buffer */ | ||
509 | static int prerecording_max_seconds; /* Max number of seconds to store */ | ||
510 | static int prerecord_count; /* Number of seconds in the prerecord buffer */ | ||
511 | static int prerecord_timeout; /* The tick count of the next prerecord data store */ | ||
500 | #endif | 512 | #endif |
501 | 513 | ||
502 | static int mpeg_file; | 514 | static int mpeg_file; |
@@ -865,17 +877,38 @@ static void dma_tick(void) | |||
865 | 877 | ||
866 | num_rec_bytes += i; | 878 | num_rec_bytes += i; |
867 | 879 | ||
868 | /* Signal to save the data if we are running out of buffer | 880 | if(is_prerecording) |
869 | space */ | 881 | { |
870 | num_bytes = mp3buf_write - mp3buf_read; | 882 | if(TIME_AFTER(current_tick, prerecord_timeout)) |
871 | if(num_bytes < 0) | 883 | { |
872 | num_bytes += mp3buflen; | 884 | prerecord_timeout = current_tick + HZ; |
885 | |||
886 | /* Store the write pointer every second */ | ||
887 | prerecord_buffer[prerecord_index++] = mp3buf_write; | ||
888 | |||
889 | /* Wrap if necessary */ | ||
890 | if(prerecord_index == prerecording_max_seconds) | ||
891 | prerecord_index = 0; | ||
873 | 892 | ||
874 | if(mp3buflen - num_bytes < MPEG_RECORDING_LOW_WATER && !saving) | 893 | /* Update the number of seconds recorded */ |
894 | if(prerecord_count < prerecording_max_seconds) | ||
895 | prerecord_count++; | ||
896 | } | ||
897 | } | ||
898 | else | ||
875 | { | 899 | { |
876 | saving = true; | 900 | /* Signal to save the data if we are running out of buffer |
877 | queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); | 901 | space */ |
878 | wake_up_thread(); | 902 | num_bytes = mp3buf_write - mp3buf_read; |
903 | if(num_bytes < 0) | ||
904 | num_bytes += mp3buflen; | ||
905 | |||
906 | if(mp3buflen - num_bytes < MPEG_RECORDING_LOW_WATER && !saving) | ||
907 | { | ||
908 | saving = true; | ||
909 | queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); | ||
910 | wake_up_thread(); | ||
911 | } | ||
879 | } | 912 | } |
880 | } | 913 | } |
881 | } | 914 | } |
@@ -1887,20 +1920,84 @@ static void mpeg_thread(void) | |||
1887 | switch(ev.id) | 1920 | switch(ev.id) |
1888 | { | 1921 | { |
1889 | case MPEG_RECORD: | 1922 | case MPEG_RECORD: |
1890 | DEBUGF("Recording...\n"); | 1923 | if(is_prerecording) |
1891 | reset_mp3_buffer(); | 1924 | { |
1925 | int startpos, i; | ||
1926 | |||
1927 | /* Go back prerecord_count seconds in the buffer */ | ||
1928 | startpos = prerecord_index - prerecord_count; | ||
1929 | if(startpos < 0) | ||
1930 | startpos += prerecording_max_seconds; | ||
1931 | |||
1932 | /* Read the mp3 buffer pointer from the prerecord buffer */ | ||
1933 | startpos = prerecord_buffer[startpos]; | ||
1934 | |||
1935 | DEBUGF("Start looking at address %x (%x)\n", | ||
1936 | mp3buf+startpos, startpos); | ||
1937 | |||
1938 | saved_header = get_last_recorded_header(); | ||
1939 | |||
1940 | mem_find_next_frame(startpos, &offset, 5000, | ||
1941 | saved_header); | ||
1942 | |||
1943 | mp3buf_read = startpos + offset; | ||
1944 | |||
1945 | DEBUGF("New mp3buf_read address: %x (%x)\n", | ||
1946 | mp3buf+mp3buf_read, mp3buf_read); | ||
1892 | 1947 | ||
1893 | /* Advance the write pointer to make | 1948 | /* Make room for headers */ |
1894 | room for an ID3 tag plus a VBR header */ | 1949 | mp3buf_read -= MPEG_RESERVED_HEADER_SPACE; |
1895 | mp3buf_write = MPEG_RESERVED_HEADER_SPACE; | 1950 | if(mp3buf_read < 0) |
1896 | memset(mp3buf, 0, MPEG_RESERVED_HEADER_SPACE); | 1951 | { |
1952 | /* Clear the bottom half */ | ||
1953 | memset(mp3buf, 0, | ||
1954 | mp3buf_read + MPEG_RESERVED_HEADER_SPACE); | ||
1897 | 1955 | ||
1898 | /* Insert the ID3 header */ | 1956 | /* And the top half */ |
1899 | memcpy(mp3buf, empty_id3_header, sizeof(empty_id3_header)); | 1957 | mp3buf_read += mp3buflen; |
1958 | memset(mp3buf + mp3buf_read, 0, | ||
1959 | mp3buflen - mp3buf_read); | ||
1960 | } | ||
1961 | else | ||
1962 | { | ||
1963 | memset(mp3buf + mp3buf_read, 0, | ||
1964 | MPEG_RESERVED_HEADER_SPACE); | ||
1965 | } | ||
1966 | |||
1967 | /* Copy the empty ID3 header */ | ||
1968 | startpos = mp3buf_read; | ||
1969 | for(i = 0;i < (int)sizeof(empty_id3_header);i++) | ||
1970 | { | ||
1971 | mp3buf[startpos++] = empty_id3_header[i]; | ||
1972 | if(startpos == mp3buflen) | ||
1973 | startpos = 0; | ||
1974 | } | ||
1975 | |||
1976 | DEBUGF("New mp3buf_read address (reservation): %x\n", | ||
1977 | mp3buf+mp3buf_read); | ||
1978 | |||
1979 | DEBUGF("Prerecording...\n"); | ||
1980 | } | ||
1981 | else | ||
1982 | { | ||
1983 | reset_mp3_buffer(); | ||
1984 | |||
1985 | num_rec_bytes = 0; | ||
1986 | |||
1987 | /* Advance the write pointer to make | ||
1988 | room for an ID3 tag plus a VBR header */ | ||
1989 | mp3buf_write = MPEG_RESERVED_HEADER_SPACE; | ||
1990 | memset(mp3buf, 0, MPEG_RESERVED_HEADER_SPACE); | ||
1991 | |||
1992 | /* Insert the ID3 header */ | ||
1993 | memcpy(mp3buf, empty_id3_header, | ||
1994 | sizeof(empty_id3_header)); | ||
1995 | |||
1996 | DEBUGF("Recording...\n"); | ||
1997 | } | ||
1900 | 1998 | ||
1901 | start_recording(); | 1999 | start_recording(); |
1902 | demand_irq_enable(true); | 2000 | |
1903 | |||
1904 | mpeg_file = open(recording_filename, O_WRONLY|O_CREAT); | 2001 | mpeg_file = open(recording_filename, O_WRONLY|O_CREAT); |
1905 | 2002 | ||
1906 | if(mpeg_file < 0) | 2003 | if(mpeg_file < 0) |
@@ -1910,14 +2007,13 @@ static void mpeg_thread(void) | |||
1910 | 2007 | ||
1911 | case MPEG_STOP: | 2008 | case MPEG_STOP: |
1912 | DEBUGF("MPEG_STOP\n"); | 2009 | DEBUGF("MPEG_STOP\n"); |
1913 | demand_irq_enable(false); | ||
1914 | 2010 | ||
1915 | /* Store the last recorded header for later use by the | 2011 | /* Store the last recorded header for later use by the |
1916 | Xing header generation */ | 2012 | Xing header generation */ |
1917 | saved_header = get_last_recorded_header(); | 2013 | saved_header = get_last_recorded_header(); |
1918 | 2014 | ||
1919 | stop_recording(); | 2015 | stop_recording(); |
1920 | 2016 | ||
1921 | /* Save the remaining data in the buffer */ | 2017 | /* Save the remaining data in the buffer */ |
1922 | stop_pending = true; | 2018 | stop_pending = true; |
1923 | queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); | 2019 | queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); |
@@ -1942,6 +2038,11 @@ static void mpeg_thread(void) | |||
1942 | if(num_recorded_frames == 0x7ffff) | 2038 | if(num_recorded_frames == 0x7ffff) |
1943 | num_recorded_frames = 0; | 2039 | num_recorded_frames = 0; |
1944 | 2040 | ||
2041 | /* Also, if we have been prerecording, the frame count | ||
2042 | will be wrong */ | ||
2043 | if(prerecording) | ||
2044 | num_recorded_frames = 0; | ||
2045 | |||
1945 | /* saved_header is saved right before stopping | 2046 | /* saved_header is saved right before stopping |
1946 | the MAS */ | 2047 | the MAS */ |
1947 | framelen = create_xing_header(mpeg_file, 0, | 2048 | framelen = create_xing_header(mpeg_file, 0, |
@@ -1970,6 +2071,11 @@ static void mpeg_thread(void) | |||
1970 | } | 2071 | } |
1971 | } | 2072 | } |
1972 | #endif | 2073 | #endif |
2074 | |||
2075 | if(prerecording) | ||
2076 | { | ||
2077 | start_prerecording(); | ||
2078 | } | ||
1973 | mpeg_stop_done = true; | 2079 | mpeg_stop_done = true; |
1974 | break; | 2080 | break; |
1975 | 2081 | ||
@@ -2117,7 +2223,6 @@ static void mpeg_thread(void) | |||
2117 | if(errno == ENOSPC) | 2223 | if(errno == ENOSPC) |
2118 | { | 2224 | { |
2119 | mpeg_errno = MPEGERR_DISK_FULL; | 2225 | mpeg_errno = MPEGERR_DISK_FULL; |
2120 | demand_irq_enable(false); | ||
2121 | stop_recording(); | 2226 | stop_recording(); |
2122 | queue_post(&mpeg_queue, MPEG_STOP_DONE, 0); | 2227 | queue_post(&mpeg_queue, MPEG_STOP_DONE, 0); |
2123 | break; | 2228 | break; |
@@ -2161,18 +2266,22 @@ static void mpeg_thread(void) | |||
2161 | break; | 2266 | break; |
2162 | 2267 | ||
2163 | case SYS_USB_CONNECTED: | 2268 | case SYS_USB_CONNECTED: |
2164 | is_playing = false; | 2269 | /* We can safely go to USB mode if no recording |
2165 | paused = false; | 2270 | is taking place */ |
2166 | stop_playing(); | 2271 | if((!is_recording || is_prerecording) && mpeg_stop_done) |
2167 | #ifndef SIMULATOR | 2272 | { |
2273 | /* Even if we aren't recording, we still call this | ||
2274 | function, to put the MAS in monitoring mode, | ||
2275 | to save power. */ | ||
2276 | stop_recording(); | ||
2168 | 2277 | ||
2169 | /* Tell the USB thread that we are safe */ | 2278 | /* Tell the USB thread that we are safe */ |
2170 | DEBUGF("mpeg_thread got SYS_USB_CONNECTED\n"); | 2279 | DEBUGF("mpeg_thread got SYS_USB_CONNECTED\n"); |
2171 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | 2280 | usb_acknowledge(SYS_USB_CONNECTED_ACK); |
2172 | 2281 | ||
2173 | /* Wait until the USB cable is extracted again */ | 2282 | /* Wait until the USB cable is extracted again */ |
2174 | usb_wait_for_disconnect(&mpeg_queue); | 2283 | usb_wait_for_disconnect(&mpeg_queue); |
2175 | #endif | 2284 | } |
2176 | break; | 2285 | break; |
2177 | } | 2286 | } |
2178 | } | 2287 | } |
@@ -2245,22 +2354,90 @@ bool mpeg_has_changed_track(void) | |||
2245 | } | 2354 | } |
2246 | 2355 | ||
2247 | #ifdef HAVE_MAS3587F | 2356 | #ifdef HAVE_MAS3587F |
2248 | void mpeg_init_recording(void) | 2357 | void mpeg_init_playback(void) |
2249 | { | 2358 | { |
2250 | init_recording_done = false; | 2359 | init_playback_done = false; |
2251 | queue_post(&mpeg_queue, MPEG_INIT_RECORDING, NULL); | 2360 | queue_post(&mpeg_queue, MPEG_INIT_PLAYBACK, NULL); |
2252 | 2361 | ||
2253 | while(!init_recording_done) | 2362 | while(!init_playback_done) |
2254 | sleep_thread(); | 2363 | sleep_thread(); |
2255 | wake_up_thread(); | 2364 | wake_up_thread(); |
2256 | } | 2365 | } |
2257 | 2366 | ||
2258 | void mpeg_init_playback(void) | 2367 | static void init_playback(void) |
2259 | { | 2368 | { |
2260 | init_playback_done = false; | 2369 | unsigned long val; |
2261 | queue_post(&mpeg_queue, MPEG_INIT_PLAYBACK, NULL); | 2370 | int rc; |
2262 | 2371 | ||
2263 | while(!init_playback_done) | 2372 | if(mpeg_mode == MPEG_ENCODER) |
2373 | stop_recording(); | ||
2374 | |||
2375 | stop_dma(); | ||
2376 | |||
2377 | mas_reset(); | ||
2378 | |||
2379 | /* Enable the audio CODEC and the DSP core, max analog voltage range */ | ||
2380 | rc = mas_direct_config_write(MAS_CONTROL, 0x8c00); | ||
2381 | if(rc < 0) | ||
2382 | panicf("mas_ctrl_w: %d", rc); | ||
2383 | |||
2384 | /* Stop the current application */ | ||
2385 | val = 0; | ||
2386 | mas_writemem(MAS_BANK_D0,0x7f6,&val,1); | ||
2387 | do | ||
2388 | { | ||
2389 | mas_readmem(MAS_BANK_D0, 0x7f7, &val, 1); | ||
2390 | } while(val); | ||
2391 | |||
2392 | /* Enable the D/A Converter */ | ||
2393 | mas_codec_writereg(0x0, 0x0001); | ||
2394 | |||
2395 | /* ADC scale 0%, DSP scale 100% */ | ||
2396 | mas_codec_writereg(6, 0x0000); | ||
2397 | mas_codec_writereg(7, 0x4000); | ||
2398 | |||
2399 | /* Disable SDO and SDI */ | ||
2400 | val = 0x0d; | ||
2401 | mas_writemem(MAS_BANK_D0,0x7f2,&val,1); | ||
2402 | |||
2403 | /* Set Demand mode and validate all settings */ | ||
2404 | val = 0x25; | ||
2405 | mas_writemem(MAS_BANK_D0,0x7f1,&val,1); | ||
2406 | |||
2407 | /* Start the Layer2/3 decoder applications */ | ||
2408 | val = 0x0c; | ||
2409 | mas_writemem(MAS_BANK_D0,0x7f6,&val,1); | ||
2410 | do | ||
2411 | { | ||
2412 | mas_readmem(MAS_BANK_D0, 0x7f7, &val, 1); | ||
2413 | } while((val & 0x0c) != 0x0c); | ||
2414 | |||
2415 | mpeg_sound_channel_config(MPEG_SOUND_STEREO); | ||
2416 | |||
2417 | mpeg_mode = MPEG_DECODER; | ||
2418 | |||
2419 | /* set IRQ6 to edge detect */ | ||
2420 | ICR |= 0x02; | ||
2421 | |||
2422 | /* set IRQ6 prio 8 */ | ||
2423 | IPRB = ( IPRB & 0xff0f ) | 0x0080; | ||
2424 | |||
2425 | DEBUGF("MAS Decoding application started\n"); | ||
2426 | } | ||
2427 | |||
2428 | /**************************************************************************** | ||
2429 | ** | ||
2430 | ** | ||
2431 | ** Recording functions | ||
2432 | ** | ||
2433 | ** | ||
2434 | ***************************************************************************/ | ||
2435 | void mpeg_init_recording(void) | ||
2436 | { | ||
2437 | init_recording_done = false; | ||
2438 | queue_post(&mpeg_queue, MPEG_INIT_RECORDING, NULL); | ||
2439 | |||
2440 | while(!init_recording_done) | ||
2264 | sleep_thread(); | 2441 | sleep_thread(); |
2265 | wake_up_thread(); | 2442 | wake_up_thread(); |
2266 | } | 2443 | } |
@@ -2270,6 +2447,9 @@ static void init_recording(void) | |||
2270 | unsigned long val; | 2447 | unsigned long val; |
2271 | int rc; | 2448 | int rc; |
2272 | 2449 | ||
2450 | /* Disable IRQ6 */ | ||
2451 | IPRB &= 0xff0f; | ||
2452 | |||
2273 | stop_playing(); | 2453 | stop_playing(); |
2274 | is_playing = false; | 2454 | is_playing = false; |
2275 | paused = false; | 2455 | paused = false; |
@@ -2283,6 +2463,9 @@ static void init_recording(void) | |||
2283 | 2463 | ||
2284 | /* Init the recording variables */ | 2464 | /* Init the recording variables */ |
2285 | is_recording = false; | 2465 | is_recording = false; |
2466 | is_prerecording = false; | ||
2467 | |||
2468 | mpeg_stop_done = true; | ||
2286 | 2469 | ||
2287 | mas_reset(); | 2470 | mas_reset(); |
2288 | 2471 | ||
@@ -2324,7 +2507,7 @@ static void init_recording(void) | |||
2324 | 2507 | ||
2325 | /* No mute */ | 2508 | /* No mute */ |
2326 | val = 0; | 2509 | val = 0; |
2327 | mas_writemem(MAS_BANK_D0, 0x7f9, &val, 1); | 2510 | mas_writemem(MAS_BANK_D0, 0x7f9, &val, 1); |
2328 | 2511 | ||
2329 | /* Set Demand mode, no monitoring and validate all settings */ | 2512 | /* Set Demand mode, no monitoring and validate all settings */ |
2330 | val = 0x125; | 2513 | val = 0x125; |
@@ -2345,70 +2528,16 @@ static void init_recording(void) | |||
2345 | Now let's wait for some data to be encoded. */ | 2528 | Now let's wait for some data to be encoded. */ |
2346 | sleep(20); | 2529 | sleep(20); |
2347 | 2530 | ||
2348 | /* Disable IRQ6 */ | 2531 | /* Now set it to Monitoring mode as default, saves power */ |
2349 | IPRB &= 0xff0f; | 2532 | val = 0x525; |
2533 | mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1); | ||
2350 | 2534 | ||
2351 | mpeg_mode = MPEG_ENCODER; | 2535 | mpeg_mode = MPEG_ENCODER; |
2352 | 2536 | ||
2353 | DEBUGF("MAS Recording application started\n"); | 2537 | DEBUGF("MAS Recording application started\n"); |
2354 | } | ||
2355 | |||
2356 | static void init_playback(void) | ||
2357 | { | ||
2358 | unsigned long val; | ||
2359 | int rc; | ||
2360 | |||
2361 | stop_dma(); | ||
2362 | |||
2363 | mas_reset(); | ||
2364 | |||
2365 | /* Enable the audio CODEC and the DSP core, max analog voltage range */ | ||
2366 | rc = mas_direct_config_write(MAS_CONTROL, 0x8c00); | ||
2367 | if(rc < 0) | ||
2368 | panicf("mas_ctrl_w: %d", rc); | ||
2369 | |||
2370 | /* Stop the current application */ | ||
2371 | val = 0; | ||
2372 | mas_writemem(MAS_BANK_D0,0x7f6,&val,1); | ||
2373 | do | ||
2374 | { | ||
2375 | mas_readmem(MAS_BANK_D0, 0x7f7, &val, 1); | ||
2376 | } while(val); | ||
2377 | |||
2378 | /* Enable the D/A Converter */ | ||
2379 | mas_codec_writereg(0x0, 0x0001); | ||
2380 | |||
2381 | /* ADC scale 0%, DSP scale 100% */ | ||
2382 | mas_codec_writereg(6, 0x0000); | ||
2383 | mas_codec_writereg(7, 0x4000); | ||
2384 | |||
2385 | /* Disable SDO and SDI */ | ||
2386 | val = 0x0d; | ||
2387 | mas_writemem(MAS_BANK_D0,0x7f2,&val,1); | ||
2388 | |||
2389 | /* Set Demand mode and validate all settings */ | ||
2390 | val = 0x25; | ||
2391 | mas_writemem(MAS_BANK_D0,0x7f1,&val,1); | ||
2392 | |||
2393 | /* Start the Layer2/3 decoder applications */ | ||
2394 | val = 0x0c; | ||
2395 | mas_writemem(MAS_BANK_D0,0x7f6,&val,1); | ||
2396 | do | ||
2397 | { | ||
2398 | mas_readmem(MAS_BANK_D0, 0x7f7, &val, 1); | ||
2399 | } while((val & 0x0c) != 0x0c); | ||
2400 | |||
2401 | mpeg_sound_channel_config(MPEG_SOUND_STEREO); | ||
2402 | |||
2403 | mpeg_mode = MPEG_DECODER; | ||
2404 | |||
2405 | /* set IRQ6 to edge detect */ | ||
2406 | ICR |= 0x02; | ||
2407 | |||
2408 | /* set IRQ6 prio 8 */ | ||
2409 | IPRB = ( IPRB & 0xff0f ) | 0x0080; | ||
2410 | 2538 | ||
2411 | DEBUGF("MAS Decoding application started\n"); | 2539 | /* At this point, all settings are the reset MAS defaults, next thing is to |
2540 | call mpeg_set_recording_options(). */ | ||
2412 | } | 2541 | } |
2413 | 2542 | ||
2414 | void mpeg_record(char *filename) | 2543 | void mpeg_record(char *filename) |
@@ -2418,53 +2547,109 @@ void mpeg_record(char *filename) | |||
2418 | strncpy(recording_filename, filename, MAX_PATH - 1); | 2547 | strncpy(recording_filename, filename, MAX_PATH - 1); |
2419 | recording_filename[MAX_PATH - 1] = 0; | 2548 | recording_filename[MAX_PATH - 1] = 0; |
2420 | 2549 | ||
2421 | num_rec_bytes = 0; | ||
2422 | disable_xing_header = false; | 2550 | disable_xing_header = false; |
2423 | queue_post(&mpeg_queue, MPEG_RECORD, NULL); | 2551 | queue_post(&mpeg_queue, MPEG_RECORD, NULL); |
2424 | } | 2552 | } |
2425 | 2553 | ||
2426 | static void start_recording(void) | 2554 | static void start_prerecording(void) |
2427 | { | 2555 | { |
2428 | unsigned long val; | 2556 | unsigned long val; |
2429 | 2557 | ||
2430 | num_recorded_frames = 0; | 2558 | DEBUGF("Starting prerecording\n"); |
2431 | 2559 | ||
2432 | /* Stop monitoring and record for real */ | 2560 | prerecord_index = 0; |
2561 | prerecord_count = 0; | ||
2562 | prerecord_timeout = current_tick + HZ; | ||
2563 | memset(prerecord_buffer, 0, sizeof(prerecord_buffer)); | ||
2564 | reset_mp3_buffer(); | ||
2565 | |||
2566 | is_prerecording = true; | ||
2567 | |||
2568 | /* Stop monitoring and start the encoder */ | ||
2433 | mas_readmem(MAS_BANK_D0, 0x7f1, &val, 1); | 2569 | mas_readmem(MAS_BANK_D0, 0x7f1, &val, 1); |
2434 | val &= ~(1 << 10); | 2570 | val &= ~(1 << 10); |
2435 | val |= 1; | 2571 | val |= 1; |
2436 | mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1); | 2572 | mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1); |
2573 | DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f1, %x)\n", val); | ||
2437 | 2574 | ||
2438 | /* Wait until the DSP has accepted the settings */ | 2575 | /* Wait until the DSP has accepted the settings */ |
2439 | do | 2576 | do |
2440 | { | 2577 | { |
2441 | mas_readmem(MAS_BANK_D0, 0x7f1, &val,1); | 2578 | mas_readmem(MAS_BANK_D0, 0x7f1, &val,1); |
2442 | } while(val & 1); | 2579 | } while(val & 1); |
2443 | |||
2444 | sleep(20); | ||
2445 | 2580 | ||
2446 | /* Store the current time */ | 2581 | sleep(20); |
2447 | record_start_time = current_tick; | ||
2448 | 2582 | ||
2449 | is_recording = true; | 2583 | is_recording = true; |
2450 | stop_pending = false; | 2584 | stop_pending = false; |
2451 | saving = false; | 2585 | saving = false; |
2586 | |||
2587 | demand_irq_enable(true); | ||
2588 | } | ||
2589 | |||
2590 | static void start_recording(void) | ||
2591 | { | ||
2592 | unsigned long val; | ||
2593 | |||
2594 | num_recorded_frames = 0; | ||
2595 | |||
2596 | if(is_prerecording) | ||
2597 | { | ||
2598 | /* This will make the IRQ handler start recording | ||
2599 | for real, i.e send MPEG_SAVE_DATA messages when | ||
2600 | the buffer is full */ | ||
2601 | is_prerecording = false; | ||
2602 | } | ||
2603 | else | ||
2604 | { | ||
2605 | /* If prerecording is off, we need to stop the monitoring | ||
2606 | and start the encoder */ | ||
2607 | mas_readmem(MAS_BANK_D0, 0x7f1, &val, 1); | ||
2608 | val &= ~(1 << 10); | ||
2609 | val |= 1; | ||
2610 | mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1); | ||
2611 | DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f1, %x)\n", val); | ||
2612 | |||
2613 | /* Wait until the DSP has accepted the settings */ | ||
2614 | do | ||
2615 | { | ||
2616 | mas_readmem(MAS_BANK_D0, 0x7f1, &val,1); | ||
2617 | } while(val & 1); | ||
2618 | |||
2619 | sleep(20); | ||
2620 | } | ||
2621 | |||
2622 | is_recording = true; | ||
2623 | stop_pending = false; | ||
2624 | saving = false; | ||
2625 | |||
2626 | /* Store the current time */ | ||
2627 | if(prerecording) | ||
2628 | record_start_time = current_tick - prerecord_count * HZ; | ||
2629 | else | ||
2630 | record_start_time = current_tick; | ||
2631 | |||
2632 | demand_irq_enable(true); | ||
2452 | } | 2633 | } |
2453 | 2634 | ||
2454 | static void stop_recording(void) | 2635 | static void stop_recording(void) |
2455 | { | 2636 | { |
2456 | unsigned long val; | 2637 | unsigned long val; |
2457 | 2638 | ||
2458 | is_recording = false; | 2639 | demand_irq_enable(false); |
2459 | 2640 | ||
2641 | is_recording = false; | ||
2642 | is_prerecording = false; | ||
2643 | |||
2460 | /* Read the number of frames recorded */ | 2644 | /* Read the number of frames recorded */ |
2461 | mas_readmem(MAS_BANK_D0, 0xfd0, &num_recorded_frames, 1); | 2645 | mas_readmem(MAS_BANK_D0, 0xfd0, &num_recorded_frames, 1); |
2462 | 2646 | ||
2463 | /* Start monitoring */ | 2647 | /* Start monitoring */ |
2464 | mas_readmem(MAS_BANK_D0, 0x7f1, &val, 1); | 2648 | mas_readmem(MAS_BANK_D0, 0x7f1, &val, 1); |
2465 | val |= (1 << 10) | 1; | 2649 | val |= (1 << 10) | 1; |
2466 | mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1); | 2650 | mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1); |
2467 | 2651 | DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f1, %x)\n", val); | |
2652 | |||
2468 | /* Wait until the DSP has accepted the settings */ | 2653 | /* Wait until the DSP has accepted the settings */ |
2469 | do | 2654 | do |
2470 | { | 2655 | { |
@@ -2474,6 +2659,82 @@ static void stop_recording(void) | |||
2474 | drain_dma_buffer(); | 2659 | drain_dma_buffer(); |
2475 | } | 2660 | } |
2476 | 2661 | ||
2662 | void mpeg_set_recording_options(int frequency, int quality, | ||
2663 | int source, int channel_mode, | ||
2664 | bool editable, int prerecord_time) | ||
2665 | { | ||
2666 | bool is_mpeg1; | ||
2667 | unsigned long val; | ||
2668 | |||
2669 | is_mpeg1 = (frequency < 3)?true:false; | ||
2670 | |||
2671 | rec_version_index = is_mpeg1?3:2; | ||
2672 | rec_frequency_index = frequency % 3; | ||
2673 | |||
2674 | val = (quality << 17) | | ||
2675 | (rec_frequency_index << 10) | | ||
2676 | ((is_mpeg1?1:0) << 9) | | ||
2677 | (1 << 8) | /* CRC on */ | ||
2678 | (((channel_mode * 2 + 1) & 3) << 6) | | ||
2679 | (1 << 5) /* MS-stereo */ | | ||
2680 | (1 << 2) /* Is an original */; | ||
2681 | mas_writemem(MAS_BANK_D0, 0x7f0, &val,1); | ||
2682 | |||
2683 | DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f0, %x)\n", val); | ||
2684 | |||
2685 | val = editable?4:0; | ||
2686 | mas_writemem(MAS_BANK_D0, 0x7f9, &val,1); | ||
2687 | |||
2688 | DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f9, %x)\n", val); | ||
2689 | |||
2690 | val = (((source < 2)?1:2) << 8) | /* Input select */ | ||
2691 | (1 << 5) | /* SDO strobe invert */ | ||
2692 | ((is_mpeg1?0:1) << 3) | | ||
2693 | (1 << 2) | /* Inverted SIBC clock signal */ | ||
2694 | 1; /* Validate */ | ||
2695 | mas_writemem(MAS_BANK_D0, 0x7f1, &val,1); | ||
2696 | |||
2697 | DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f1, %x)\n", val); | ||
2698 | |||
2699 | drain_dma_buffer(); | ||
2700 | |||
2701 | if(source == 0) /* Mic */ | ||
2702 | { | ||
2703 | /* Copy left channel to right (mono mode) */ | ||
2704 | mas_codec_writereg(8, 0x8000); | ||
2705 | } | ||
2706 | else | ||
2707 | { | ||
2708 | /* Stereo input mode */ | ||
2709 | mas_codec_writereg(8, 0); | ||
2710 | } | ||
2711 | |||
2712 | prerecording_max_seconds = prerecord_time; | ||
2713 | if(prerecording_max_seconds) | ||
2714 | { | ||
2715 | prerecording = true; | ||
2716 | start_prerecording(); | ||
2717 | } | ||
2718 | else | ||
2719 | { | ||
2720 | prerecording = false; | ||
2721 | is_prerecording = false; | ||
2722 | is_recording = false; | ||
2723 | } | ||
2724 | } | ||
2725 | |||
2726 | /* If use_mic is true, the left gain is used */ | ||
2727 | void mpeg_set_recording_gain(int left, int right, bool use_mic) | ||
2728 | { | ||
2729 | /* Enable both left and right A/D */ | ||
2730 | mas_codec_writereg(0x0, | ||
2731 | (left << 12) | | ||
2732 | (right << 8) | | ||
2733 | (left << 4) | | ||
2734 | (use_mic?0x0008:0) | /* Connect left A/D to mic */ | ||
2735 | 0x0007); | ||
2736 | } | ||
2737 | |||
2477 | void mpeg_new_file(char *filename) | 2738 | void mpeg_new_file(char *filename) |
2478 | { | 2739 | { |
2479 | mpeg_errno = 0; | 2740 | mpeg_errno = 0; |
@@ -2492,16 +2753,37 @@ void mpeg_new_file(char *filename) | |||
2492 | 2753 | ||
2493 | unsigned long mpeg_recorded_time(void) | 2754 | unsigned long mpeg_recorded_time(void) |
2494 | { | 2755 | { |
2756 | if(is_prerecording) | ||
2757 | return prerecord_count * HZ; | ||
2758 | |||
2495 | if(is_recording) | 2759 | if(is_recording) |
2496 | return current_tick - record_start_time; | 2760 | return current_tick - record_start_time; |
2497 | else | 2761 | |
2498 | return 0; | 2762 | return 0; |
2499 | } | 2763 | } |
2500 | 2764 | ||
2501 | unsigned long mpeg_num_recorded_bytes(void) | 2765 | unsigned long mpeg_num_recorded_bytes(void) |
2502 | { | 2766 | { |
2767 | int num_bytes; | ||
2768 | int index; | ||
2769 | |||
2503 | if(is_recording) | 2770 | if(is_recording) |
2504 | return num_rec_bytes; | 2771 | { |
2772 | if(is_prerecording) | ||
2773 | { | ||
2774 | index = prerecord_index - prerecord_count; | ||
2775 | if(index < 0) | ||
2776 | index += prerecording_max_seconds; | ||
2777 | |||
2778 | num_bytes = mp3buf_write - prerecord_buffer[index]; | ||
2779 | if(num_bytes < 0) | ||
2780 | num_bytes += mp3buflen; | ||
2781 | |||
2782 | return num_bytes;; | ||
2783 | } | ||
2784 | else | ||
2785 | return num_rec_bytes; | ||
2786 | } | ||
2505 | else | 2787 | else |
2506 | return 0; | 2788 | return 0; |
2507 | } | 2789 | } |
@@ -2660,8 +2942,11 @@ int mpeg_status(void) | |||
2660 | ret |= MPEG_STATUS_PAUSE; | 2942 | ret |= MPEG_STATUS_PAUSE; |
2661 | 2943 | ||
2662 | #ifdef HAVE_MAS3587F | 2944 | #ifdef HAVE_MAS3587F |
2663 | if(is_recording) | 2945 | if(is_recording && !is_prerecording) |
2664 | ret |= MPEG_STATUS_RECORD; | 2946 | ret |= MPEG_STATUS_RECORD; |
2947 | |||
2948 | if(is_prerecording) | ||
2949 | ret |= MPEG_STATUS_PRERECORD; | ||
2665 | #endif | 2950 | #endif |
2666 | 2951 | ||
2667 | if(mpeg_errno) | 2952 | if(mpeg_errno) |
@@ -3057,73 +3342,6 @@ void mpeg_set_pitch(int pitch) | |||
3057 | } | 3342 | } |
3058 | #endif | 3343 | #endif |
3059 | 3344 | ||
3060 | #ifdef HAVE_MAS3587F | ||
3061 | void mpeg_set_recording_options(int frequency, int quality, | ||
3062 | int source, int channel_mode, | ||
3063 | bool editable) | ||
3064 | { | ||
3065 | bool is_mpeg1; | ||
3066 | unsigned long val; | ||
3067 | |||
3068 | is_mpeg1 = (frequency < 3)?true:false; | ||
3069 | |||
3070 | rec_version_index = is_mpeg1?3:2; | ||
3071 | rec_frequency_index = frequency % 3; | ||
3072 | |||
3073 | val = (quality << 17) | | ||
3074 | (rec_frequency_index << 10) | | ||
3075 | ((is_mpeg1?1:0) << 9) | | ||
3076 | (1 << 8) | /* CRC on */ | ||
3077 | (((channel_mode * 2 + 1) & 3) << 6) | | ||
3078 | (1 << 5) /* MS-stereo */ | | ||
3079 | (1 << 2) /* Is an original */; | ||
3080 | mas_writemem(MAS_BANK_D0, 0x7f0, &val,1); | ||
3081 | |||
3082 | DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f0, %x)\n", val); | ||
3083 | |||
3084 | val = editable?4:0; | ||
3085 | mas_writemem(MAS_BANK_D0, 0x7f9, &val,1); | ||
3086 | |||
3087 | DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f9, %x)\n", val); | ||
3088 | |||
3089 | val = ((!is_recording << 10) | /* Monitoring */ | ||
3090 | ((source < 2)?1:2) << 8) | /* Input select */ | ||
3091 | (1 << 5) | /* SDO strobe invert */ | ||
3092 | ((is_mpeg1?0:1) << 3) | | ||
3093 | (1 << 2) | /* Inverted SIBC clock signal */ | ||
3094 | 1; /* Validate */ | ||
3095 | mas_writemem(MAS_BANK_D0, 0x7f1, &val,1); | ||
3096 | |||
3097 | DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f1, %x)\n", val); | ||
3098 | |||
3099 | drain_dma_buffer(); | ||
3100 | |||
3101 | if(source == 0) /* Mic */ | ||
3102 | { | ||
3103 | /* Copy left channel to right (mono mode) */ | ||
3104 | mas_codec_writereg(8, 0x8000); | ||
3105 | } | ||
3106 | else | ||
3107 | { | ||
3108 | /* Stereo input mode */ | ||
3109 | mas_codec_writereg(8, 0); | ||
3110 | } | ||
3111 | } | ||
3112 | |||
3113 | /* If use_mic is true, the left gain is used */ | ||
3114 | void mpeg_set_recording_gain(int left, int right, bool use_mic) | ||
3115 | { | ||
3116 | /* Enable both left and right A/D */ | ||
3117 | mas_codec_writereg(0x0, | ||
3118 | (left << 12) | | ||
3119 | (right << 8) | | ||
3120 | (left << 4) | | ||
3121 | (use_mic?0x0008:0) | /* Connect left A/D to mic */ | ||
3122 | 0x0007); | ||
3123 | } | ||
3124 | |||
3125 | #endif | ||
3126 | |||
3127 | #ifdef SIMULATOR | 3345 | #ifdef SIMULATOR |
3128 | static char mpeg_stack[DEFAULT_STACK_SIZE]; | 3346 | static char mpeg_stack[DEFAULT_STACK_SIZE]; |
3129 | static char mpeg_thread_name[] = "mpeg"; | 3347 | static char mpeg_thread_name[] = "mpeg"; |