1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
30 #include "powermgmt.h"
34 #if CONFIG_CODEC == SWCODEC
36 #include "enc_config.h"
37 #if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT)
40 #endif /* CONFIG_CODEC == SWCODEC */
41 #include "pcm_record.h"
42 #include "recording.h"
43 #include "mp3_playback.h"
53 #include "peakmeter.h"
55 #include "sound_menu.h"
56 #include "timefuncs.h"
66 #if (CONFIG_STORAGE & STORAGE_ATA) && (CONFIG_LED == LED_REAL) \
67 && !defined(SIMULATOR)
71 #include "screen_access.h"
77 #include "appevents.h"
80 /* This array holds the record timer interval lengths, in seconds */
81 static const unsigned long rec_timer_seconds
[] =
95 10L*60*60, /* 10:00 */
96 12L*60*60, /* 12:00 */
97 18L*60*60, /* 18:00 */
101 static unsigned int rec_timesplit_seconds(void)
103 return rec_timer_seconds
[global_settings
.rec_timesplit
];
106 /* This array holds the record size interval lengths, in bytes */
107 static const unsigned long rec_size_bytes
[] =
110 5*1024*1024, /* 5MB */
111 10*1024*1024, /* 10MB */
112 15*1024*1024, /* 15MB */
113 32*1024*1024, /* 32MB */
114 64*1024*1024, /* 64MB */
115 75*1024*1024, /* 75MB */
116 100*1024*1024, /* 100MB */
117 128*1024*1024, /* 128MB */
118 256*1024*1024, /* 256MB */
119 512*1024*1024, /* 512MB */
120 650*1024*1024, /* 650MB */
121 700*1024*1024, /* 700MB */
122 1024*1024*1024, /* 1GB */
123 1536*1024*1024, /* 1.5GB */
124 1792*1024*1024, /* 1.75GB */
127 static unsigned long rec_sizesplit_bytes(void)
129 return rec_size_bytes
[global_settings
.rec_sizesplit
];
132 void settings_apply_trigger(void)
134 int start_thres
, stop_thres
;
135 if (global_settings
.peak_meter_dbfs
)
137 start_thres
= global_settings
.rec_start_thres_db
- 1;
138 stop_thres
= global_settings
.rec_stop_thres_db
- 1;
142 start_thres
= global_settings
.rec_start_thres_linear
;
143 stop_thres
= global_settings
.rec_stop_thres_linear
;
146 peak_meter_define_trigger(
148 global_settings
.rec_start_duration
*HZ
,
149 MIN(global_settings
.rec_start_duration
*HZ
/ 2, 2*HZ
),
151 global_settings
.rec_stop_postrec
*HZ
,
152 global_settings
.rec_stop_gap
*HZ
155 /* recording screen status flags */
156 enum rec_status_flags
158 RCSTAT_IN_RECSCREEN
= 0x00000001,
159 RCSTAT_BEEN_IN_USB_MODE
= 0x00000002,
160 RCSTAT_CREATED_DIRECTORY
= 0x00000004,
161 RCSTAT_HAVE_RECORDED
= 0x00000008,
164 static int rec_status
= 0;
166 bool in_recording_screen(void)
168 return (rec_status
& RCSTAT_IN_RECSCREEN
) != 0;
171 #if CONFIG_KEYPAD == RECORDER_PAD
172 static bool f2_rec_screen(void);
173 static bool f3_rec_screen(void);
176 #define MAX_FILE_SIZE 0x7F800000 /* 2 GB - 4 MB */
178 #ifndef HAVE_REMOTE_LCD
179 static const int screen_update
= NB_SCREENS
;
181 static int screen_update
= NB_SCREENS
;
182 static bool remote_display_on
= true;
185 /* as we have the ability to disable the remote, we need an alternative loop */
186 #define FOR_NB_ACTIVE_SCREENS(i) for(i = 0; i < screen_update; i++)
188 static bool update_list
= false; /* (GIU) list needs updating */
190 /** File name creation **/
192 /* current file number to assist in creating unique numbered filenames
193 without actually having to create the file on disk */
194 static int file_number
= -1;
195 #endif /* CONFIG_RTC */
197 #if CONFIG_CODEC == SWCODEC
199 #define REC_FILE_ENDING(rec_format) \
200 (audio_formats[rec_format_afmt[rec_format]].ext_list)
202 #else /* CONFIG_CODEC != SWCODEC */
204 /* default record file extension for HWCODEC */
205 #define REC_FILE_ENDING(rec_format) \
206 (audio_formats[AFMT_MPA_L3].ext_list)
208 #endif /* CONFIG_CODEC == SWCODEC */
210 /* path for current file */
211 static char path_buffer
[MAX_PATH
];
213 /** Automatic Gain Control (AGC) **/
216 * peak_time is incremented every 0.2s, every 2nd run of record screen loop.
217 * hist_time is incremented every 0.5s, display update.
218 * peak_time is the counter of the peak hold read and agc process,
219 * overflow every 13 years 8-)
221 static long peak_time
= 0;
222 static long hist_time
= 0;
224 static short peak_valid_mem
[4];
225 #define BAL_MEM_SIZE 24
226 static short balance_mem
[BAL_MEM_SIZE
];
228 /* Automatic Gain Control */
229 #define AGC_MODE_SIZE 5
230 #define AGC_SAFETY_MODE 0
232 static const char* agc_preset_str
[] =
233 { "Off", "S", "L", "D", "M", "V" };
240 #define AGC_CLIP 32766
241 #define AGC_PEAK 29883 /* fast gain reduction threshold -0.8dB */
242 #define AGC_HIGH 27254 /* accelerated gain reduction threshold -1.6dB */
243 #define AGC_IMG 823 /* threshold for balance control -32dB */
244 /* autogain high level thresholds (-3dB, -7dB, -4dB, -5dB, -5dB) */
245 static const short agc_th_hi
[AGC_MODE_SIZE
] =
246 { 23197, 14637, 21156, 18428, 18426 };
247 /* autogain low level thresholds (-14dB, -11dB, -6dB, -7dB, -8dB) */
248 static const short agc_th_lo
[AGC_MODE_SIZE
] =
249 { 6538, 9235, 16422, 14636, 13045 };
250 /* autogain threshold times [1/5s] or [200ms] */
251 static const short agc_tdrop
[AGC_MODE_SIZE
] =
252 { 900, 225, 150, 60, 8 };
253 static const short agc_trise
[AGC_MODE_SIZE
] =
254 { 9000, 750, 400, 150, 20 };
255 static const short agc_tbal
[AGC_MODE_SIZE
] =
256 { 4500, 500, 300, 100, 15 };
258 static bool agc_enable
= true;
259 static short agc_preset
;
261 static int agc_left
= 0;
262 static int agc_right
= 0;
263 /* AGC time since high target volume was exceeded */
264 static short agc_droptime
= 0;
265 /* AGC time since volume fallen below low target */
266 static short agc_risetime
= 0;
267 /* AGC balance time exceeding +/- 0.7dB */
268 static short agc_baltime
= 0;
269 /* AGC maximum gain */
270 static short agc_maxgain
;
271 #endif /* HAVE_AGC */
273 static void set_gain(void)
276 if(global_settings
.rec_source
== AUDIO_SRC_MIC
)
278 audio_set_recording_gain(global_settings
.rec_mic_gain
,
284 /* AUDIO_SRC_LINEIN, AUDIO_SRC_FMRADIO, AUDIO_SRC_SPDIF */
285 audio_set_recording_gain(global_settings
.rec_left_gain
,
286 global_settings
.rec_right_gain
,
289 /* reset the clipping indicators */
290 peak_meter_set_clip_hold(global_settings
.peak_meter_clip_hold
);
295 /* Read peak meter values & calculate balance.
296 * Returns validity of peak values.
297 * Used for automatic gain control and history diagram.
299 static bool read_peak_levels(int *peak_l
, int *peak_r
, int *balance
)
301 peak_meter_get_peakhold(peak_l
, peak_r
);
302 peak_valid_mem
[peak_time
% 3] = *peak_l
;
303 if (((peak_valid_mem
[0] == peak_valid_mem
[1]) &&
304 (peak_valid_mem
[1] == peak_valid_mem
[2])) &&
305 ((*peak_l
< 32767) || storage_disk_is_active()))
308 if (*peak_r
> *peak_l
)
309 balance_mem
[peak_time
% BAL_MEM_SIZE
] = (*peak_l
?
310 MIN((10000 * *peak_r
) / *peak_l
- 10000, 15118) : 15118);
312 balance_mem
[peak_time
% BAL_MEM_SIZE
] = (*peak_r
?
313 MAX(10000 - (10000 * *peak_l
) / *peak_r
, -15118) : -15118);
316 for (i
= 0; i
< BAL_MEM_SIZE
; i
++)
317 *balance
+= balance_mem
[i
];
318 *balance
= *balance
/ BAL_MEM_SIZE
;
323 /* AGC helper function to check if maximum gain is reached */
324 static bool agc_gain_is_max(bool left
, bool right
)
326 /* range -128...+108 [0.5dB] */
327 short gain_current_l
;
328 short gain_current_r
;
333 switch (global_settings
.rec_source
)
335 #if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
336 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN
:)
337 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO
:)
338 gain_current_l
= global_settings
.rec_left_gain
;
339 gain_current_r
= global_settings
.rec_right_gain
;
341 #endif /* LINE, FMRADIO */
342 #if defined(HAVE_MIC_REC)
345 gain_current_l
= global_settings
.rec_mic_gain
;
346 gain_current_r
= global_settings
.rec_mic_gain
;
350 return ((left
&& (gain_current_l
>= agc_maxgain
)) ||
351 (right
&& (gain_current_r
>= agc_maxgain
)));
354 static void change_recording_gain(bool increment
, bool left
, bool right
)
356 int factor
= (increment
? 1 : -1);
358 switch (global_settings
.rec_source
)
360 #if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
361 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN
:)
362 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO
:)
363 if (left
) global_settings
.rec_left_gain
+= factor
;
364 if (right
) global_settings
.rec_right_gain
+= factor
;
366 #endif /* LINE, FMRADIO */
367 #if defined(HAVE_MIC_REC)
369 global_settings
.rec_mic_gain
+= factor
;
375 * Handle automatic gain control (AGC).
376 * Change recording gain if peak_x levels are above or below
377 * target volume for specified timeouts.
379 static void auto_gain_control(int *peak_l
, int *peak_r
, int *balance
)
385 if (*peak_l
> agc_left
)
388 agc_left
-= (agc_left
- *peak_l
+ 3) >> 2;
389 if (*peak_r
> agc_right
)
392 agc_right
-= (agc_right
- *peak_r
+ 3) >> 2;
393 agc_mono
= (agc_left
+ agc_right
) / 2;
395 agc_mode
= abs(agc_preset
) - 1;
401 if (agc_mode
!= AGC_SAFETY_MODE
) {
402 /* Automatic balance control - only if not in safety mode */
403 if ((agc_left
> AGC_IMG
) && (agc_right
> AGC_IMG
))
408 agc_baltime
-= !(peak_time
% 4); /* 0.47 - 0.75dB */
409 else if (*balance
> -4125)
410 agc_baltime
--; /* 0.75 - 3.00dB */
411 else if (*balance
> -7579)
412 agc_baltime
-= 2; /* 3.00 - 4.90dB */
414 agc_baltime
-= !(peak_time
% 8); /* 4.90 - inf dB */
416 agc_baltime
-= (peak_time
% 2);
418 else if (*balance
> 556)
421 agc_baltime
+= !(peak_time
% 4);
422 else if (*balance
< 4125)
424 else if (*balance
< 7579)
427 agc_baltime
+= !(peak_time
% 8);
429 agc_baltime
+= (peak_time
% 2);
432 if ((*balance
* agc_baltime
) < 0)
435 agc_baltime
-= peak_time
% 2;
437 agc_baltime
+= peak_time
% 2;
440 increment
= ((agc_risetime
/ 2) > agc_droptime
);
442 if (agc_baltime
< -agc_tbal
[agc_mode
])
444 if (!increment
|| !agc_gain_is_max(!increment
, increment
)) {
445 change_recording_gain(increment
, !increment
, increment
);
450 else if (agc_baltime
> +agc_tbal
[agc_mode
])
452 if (!increment
|| !agc_gain_is_max(increment
, !increment
)) {
453 change_recording_gain(increment
, increment
, !increment
);
459 else if (!(hist_time
% 4))
468 /* Automatic gain control */
469 if ((agc_left
> agc_th_hi
[agc_mode
]) || (agc_right
> agc_th_hi
[agc_mode
]))
471 if ((agc_left
> AGC_CLIP
) || (agc_right
> AGC_CLIP
))
472 agc_droptime
+= agc_tdrop
[agc_mode
] /
473 (global_settings
.rec_agc_cliptime
+ 1);
474 if (agc_left
> AGC_HIGH
) {
477 if (agc_left
> AGC_PEAK
)
480 if (agc_right
> AGC_HIGH
) {
483 if (agc_right
> AGC_PEAK
)
486 if (agc_mono
> agc_th_hi
[agc_mode
])
489 agc_droptime
+= !(peak_time
% 2);
491 if (agc_droptime
>= agc_tdrop
[agc_mode
])
493 change_recording_gain(false, true, true);
498 agc_risetime
= MAX(agc_risetime
- 1, 0);
500 else if (agc_mono
< agc_th_lo
[agc_mode
])
502 if (agc_mono
< (agc_th_lo
[agc_mode
] / 8))
503 agc_risetime
+= !(peak_time
% 5);
504 else if (agc_mono
< (agc_th_lo
[agc_mode
] / 2))
509 if (agc_risetime
>= agc_trise
[agc_mode
]) {
510 if ((agc_mode
!= AGC_SAFETY_MODE
) &&
511 (!agc_gain_is_max(true, true))) {
512 change_recording_gain(true, true, true);
518 agc_droptime
= MAX(agc_droptime
- 1, 0);
520 else if (!(peak_time
% 6)) /* on target level every 1.2 sec */
522 agc_risetime
= MAX(agc_risetime
- 1, 0);
523 agc_droptime
= MAX(agc_droptime
- 1, 0);
526 #endif /* HAVE_AGC */
528 static const char* const fmtstr
[] =
530 "%c%d %s", /* no decimals */
531 "%c%d.%d %s ", /* 1 decimal */
532 "%c%d.%02d %s " /* 2 decimals */
535 static char *fmt_gain(int snd
, int val
, char *str
, int len
)
541 val
= sound_val2phys(snd
, val
);
547 numdec
= sound_numdecimals(snd
);
548 unit
= sound_unit(snd
);
552 i
= val
/ (10*numdec
);
553 d
= val
% (10*numdec
);
554 snprintf(str
, len
, fmtstr
[numdec
], sign
, i
, d
, unit
);
557 snprintf(str
, len
, fmtstr
[numdec
], sign
, val
, unit
);
562 /* the list below must match enum audio_sources in audio.h */
563 static const char* const prestr
[] =
565 HAVE_MIC_IN_([AUDIO_SRC_MIC
] = "R_MIC_",)
566 HAVE_LINE_REC_([AUDIO_SRC_LINEIN
] = "R_LINE_",)
567 HAVE_SPDIF_IN_([AUDIO_SRC_SPDIF
] = "R_SPDIF_",)
568 HAVE_FMRADIO_REC_([AUDIO_SRC_FMRADIO
] = "R_FM_",)
571 char *rec_create_filename(char *buffer
)
574 const char *pref
= "R_";
576 /* Directory existence and writeablility should have already been
577 * verified - do not pass NULL pointers to pcmrec */
579 if((unsigned)global_settings
.rec_source
< AUDIO_NUM_SOURCES
)
581 pref
= prestr
[global_settings
.rec_source
];
584 strcpy(buffer
, global_settings
.rec_directory
);
586 snprintf(ext
, sizeof(ext
), ".%s",
587 REC_FILE_ENDING(global_settings
.rec_format
));
590 return create_numbered_filename(buffer
, buffer
, pref
, ext
, 4,
593 /* We'll wait at least up to the start of the next second so no duplicate
595 return create_datetime_filename(buffer
, buffer
, pref
, ext
, true);
600 /* Hit disk to get a starting filename for the type */
601 static void rec_init_filename(void)
604 rec_create_filename(path_buffer
);
609 int rec_create_directory(void)
612 const char * const folder
= global_settings
.rec_directory
;
614 if (strcmp(folder
, "/") && !dir_exists(folder
))
620 while (action_userabort(HZ
) == false)
623 str(LANG_REC_DIR_NOT_WRITABLE
),
624 str(LANG_OFF_ABORT
));
629 rec_status
|= RCSTAT_CREATED_DIRECTORY
;
637 void rec_init_recording_options(struct audio_recording_options
*options
)
639 options
->rec_source
= global_settings
.rec_source
;
640 options
->rec_frequency
= global_settings
.rec_frequency
;
641 options
->rec_channels
= global_settings
.rec_channels
;
642 options
->rec_prerecord_time
= global_settings
.rec_prerecord_time
;
643 #if CONFIG_CODEC == SWCODEC
644 options
->rec_mono_mode
= global_settings
.rec_mono_mode
;
645 options
->rec_source_flags
= 0;
646 options
->enc_config
.rec_format
= global_settings
.rec_format
;
647 global_to_encoder_config(&options
->enc_config
);
649 options
->rec_quality
= global_settings
.rec_quality
;
650 options
->rec_editable
= global_settings
.rec_editable
;
654 #if CONFIG_CODEC == SWCODEC && !defined (SIMULATOR)
655 void rec_set_source(int source
, unsigned flags
)
657 /* Set audio input source, power up/down devices */
658 audio_set_input_source(source
, flags
);
660 /* Set peakmeters for recording or reset to playback */
661 peak_meter_playback((flags
& SRCF_RECORDING
) == 0);
662 peak_meter_enabled
= true;
664 #endif /* CONFIG_CODEC == SWCODEC && !defined (SIMULATOR) */
666 void rec_set_recording_options(struct audio_recording_options
*options
)
668 #if CONFIG_CODEC != SWCODEC
669 if (global_settings
.rec_prerecord_time
)
671 talk_buffer_steal(); /* will use the mp3 buffer */
673 #else /* == SWCODEC */
674 rec_set_source(options
->rec_source
,
675 options
->rec_source_flags
| SRCF_RECORDING
);
676 #endif /* CONFIG_CODEC != SWCODEC */
678 audio_set_recording_options(options
);
681 void rec_command(enum recording_command cmd
)
685 case RECORDING_CMD_STOP_SHUTDOWN
:
686 pm_activate_clipcount(false);
687 audio_stop_recording();
688 #if CONFIG_CODEC == SWCODEC
689 audio_close_recording();
693 case RECORDING_CMD_STOP
:
694 pm_activate_clipcount(false);
695 audio_stop_recording();
697 case RECORDING_CMD_START
:
698 /* steal mp3 buffer, create unique filename and start recording */
699 pm_reset_clipcount();
700 pm_activate_clipcount(true);
701 #if CONFIG_CODEC != SWCODEC
702 talk_buffer_steal(); /* we use the mp3 buffer */
704 audio_record(rec_create_filename(path_buffer
));
706 case RECORDING_CMD_START_NEWFILE
:
707 /* create unique filename and start recording*/
708 pm_reset_clipcount();
709 pm_activate_clipcount(true); /* just to be sure */
710 audio_new_file(rec_create_filename(path_buffer
));
712 case RECORDING_CMD_PAUSE
:
713 pm_activate_clipcount(false);
714 audio_pause_recording();
716 case RECORDING_CMD_RESUME
:
717 pm_activate_clipcount(true);
718 audio_resume_recording();
724 /* used in trigger_listerner and recording_screen */
725 static unsigned int last_seconds
= 0;
728 * Callback function so that the peak meter code can send an event
729 * to this application. This function can be passed to
730 * peak_meter_set_trigger_listener in order to activate the trigger.
732 static void trigger_listener(int trigger_status
)
734 switch (trigger_status
)
737 if(!(audio_status() & AUDIO_STATUS_RECORD
))
739 rec_status
|= RCSTAT_HAVE_RECORDED
;
740 rec_command(RECORDING_CMD_START
);
741 #if CONFIG_CODEC != SWCODEC
742 /* give control to mpeg thread so that it can start
744 yield(); yield(); yield();
748 /* if we're already recording this is a retrigger */
751 if((audio_status() & AUDIO_STATUS_PAUSE
) &&
752 (global_settings
.rec_trigger_type
== TRIG_TYPE_PAUSE
))
754 rec_command(RECORDING_CMD_RESUME
);
756 /* New file on trig start*/
757 else if (global_settings
.rec_trigger_type
!= TRIG_TYPE_NEW_FILE
)
759 rec_command(RECORDING_CMD_START_NEWFILE
);
760 /* tell recording_screen to reset the time */
766 /* A _change_ to TRIG_READY means the current recording has stopped */
768 if(audio_status() & AUDIO_STATUS_RECORD
)
770 switch(global_settings
.rec_trigger_type
)
772 case TRIG_TYPE_STOP
: /* Stop */
773 rec_command(RECORDING_CMD_STOP
);
776 case TRIG_TYPE_PAUSE
: /* Pause */
777 rec_command(RECORDING_CMD_PAUSE
);
780 case TRIG_TYPE_NEW_FILE
: /* New file on trig stop*/
781 rec_command(RECORDING_CMD_START_NEWFILE
);
782 /* tell recording_screen to reset the time */
786 case 3: /* Stop and shutdown */
787 rec_command(RECORDING_CMD_STOP_SHUTDOWN
);
791 if (global_settings
.rec_trigger_mode
!= TRIG_MODE_REARM
)
793 peak_meter_set_trigger_listener(NULL
);
794 peak_meter_trigger(false);
801 /* Stuff for drawing the screen */
803 enum rec_list_items_stereo
{
819 enum rec_list_items_mono
{
824 ITEM_AGC_MAXDB_M
= 5,
833 #ifdef HAVE_SPDIF_REC
834 enum rec_list_items_spdif
{
836 #if CONFIG_CODEC == SWCODEC
837 ITEM_SAMPLERATE_D
= 6,
847 static int listid_to_enum
[ITEM_COUNT
];
849 static const char* reclist_get_name(int selected_item
, void * data
,
850 char * buffer
, size_t buffer_len
)
856 data
= data
; /* not used */
857 if(selected_item
>= ITEM_COUNT
)
860 switch (listid_to_enum
[selected_item
])
863 snprintf(buffer
, buffer_len
, "%s: %s", str(LANG_VOLUME
),
864 fmt_gain(SOUND_VOLUME
,
865 global_settings
.volume
,
866 buf2
, sizeof(buf2
)));
870 if(global_settings
.rec_source
== AUDIO_SRC_MIC
)
872 /* Draw MIC recording gain */
873 snprintf(buffer
, buffer_len
, "%s: %s", str(LANG_GAIN
),
874 fmt_gain(SOUND_MIC_GAIN
,
875 global_settings
.rec_mic_gain
,
876 buf2
, sizeof(buf2
)));
881 int avg_gain
= (global_settings
.rec_left_gain
+
882 global_settings
.rec_right_gain
) / 2;
883 snprintf(buffer
, buffer_len
, "%s: %s", str(LANG_GAIN
),
884 fmt_gain(SOUND_LEFT_GAIN
,
886 buf2
, sizeof(buf2
)));
890 snprintf(buffer
, buffer_len
, "%s: %s",
892 fmt_gain(SOUND_LEFT_GAIN
,
893 global_settings
.rec_left_gain
,
894 buf2
, sizeof(buf2
)));
897 snprintf(buffer
, buffer_len
, "%s: %s",
898 str(LANG_GAIN_RIGHT
),
899 fmt_gain(SOUND_RIGHT_GAIN
,
900 global_settings
.rec_right_gain
,
901 buf2
, sizeof(buf2
)));
905 snprintf(buffer
, buffer_len
, "%s: %s",
906 str(LANG_RECORDING_AGC_PRESET
),
907 agc_preset_str
[agc_preset
]);
911 snprintf(buffer
, buffer_len
, "%s: %s",
912 str(LANG_RECORDING_AGC_MAXGAIN
),
913 fmt_gain(SOUND_LEFT_GAIN
,
914 agc_maxgain
, buf2
, sizeof(buf2
)));
916 else if (global_settings
.rec_source
== AUDIO_SRC_MIC
)
917 snprintf(buffer
, buffer_len
, "%s: %s (%s)",
918 str(LANG_RECORDING_AGC_MAXGAIN
),
919 fmt_gain(SOUND_MIC_GAIN
,
920 agc_maxgain
, buf2
, sizeof(buf2
)),
921 fmt_gain(SOUND_MIC_GAIN
,
922 agc_maxgain
- global_settings
.rec_mic_gain
,
923 buf3
, sizeof(buf3
)));
926 snprintf(buffer
, buffer_len
, "%s: %s (%s)",
927 str(LANG_RECORDING_AGC_MAXGAIN
),
928 fmt_gain(SOUND_LEFT_GAIN
,
929 agc_maxgain
, buf2
, sizeof(buf2
)),
930 fmt_gain(SOUND_LEFT_GAIN
,
932 (global_settings
.rec_left_gain
+
933 global_settings
.rec_right_gain
)/2,
934 buf3
, sizeof(buf3
)));
937 #if CONFIG_CODEC == SWCODEC
938 #ifdef HAVE_SPDIF_REC
939 case ITEM_SAMPLERATE_D
:
940 snprintf(buffer
, buffer_len
, "%s: %d",
941 str(LANG_RECORDING_FREQUENCY
),
942 pcm_rec_sample_rate());
948 if(audio_status() & AUDIO_STATUS_RECORD
)
950 size_t tot_len
= strlen(path_buffer
) +
951 strlen(str(LANG_RECORDING_FILENAME
)) + 1;
952 if(tot_len
> buffer_len
)
954 snprintf(buffer
, buffer_len
, "%s %s",
955 str(LANG_RECORDING_FILENAME
),
956 path_buffer
+ tot_len
- buffer_len
);
960 snprintf(buffer
, buffer_len
, "%s %s",
961 str(LANG_RECORDING_FILENAME
), path_buffer
);
966 return str(LANG_RECORDING_FILENAME
);
977 bool recording_start_automatic
= false;
979 bool recording_screen(bool no_source
)
982 int done
= -1; /* negative to re-init, positive to quit, zero to run */
983 char buf
[32]; /* for preparing strings */
984 char buf2
[32]; /* for preparing strings */
985 int w
, h
; /* character width/height */
986 int update_countdown
= 0; /* refresh counter */
987 unsigned int seconds
;
989 int audio_stat
= 0; /* status of the audio system */
990 int last_audio_stat
= -1; /* previous status so we can act on changes */
991 struct viewport vp_list
[NB_SCREENS
], vp_top
[NB_SCREENS
]; /* the viewports */
993 #if CONFIG_CODEC == SWCODEC
994 int warning_counter
= 0;
995 #define WARNING_PERIOD 7
998 #if CONFIG_CODEC == SWCODEC
999 #ifdef HAVE_SPDIF_REC
1000 unsigned long prev_sample_rate
= 0;
1004 #ifdef HAVE_FMRADIO_REC
1005 /* Radio is left on if:
1006 * 1) Is was on at the start and the initial source is FM Radio
1007 * 2) 1) and the source was never changed to something else
1009 int radio_status
= (global_settings
.rec_source
!= AUDIO_SRC_FMRADIO
) ?
1010 FMRADIO_OFF
: get_radio_status();
1012 #if (CONFIG_LED == LED_REAL)
1013 bool led_state
= false;
1014 int led_countdown
= 2;
1017 bool peak_read
= false;
1018 bool peak_valid
= false;
1023 int pm_x
[NB_SCREENS
]; /* peakmeter (and trigger bar) x pos */
1024 int pm_y
[NB_SCREENS
]; /* peakmeter y pos */
1025 int pm_h
[NB_SCREENS
]; /* peakmeter height */
1026 int trig_ypos
[NB_SCREENS
]; /* trigger bar y pos */
1027 int trig_width
[NB_SCREENS
]; /* trigger bar width */
1028 bool compact_view
[NB_SCREENS
]; /* tweak layout tiny screens / big fonts */
1030 struct gui_synclist lists
; /* the list in the bottom vp */
1031 #ifdef HAVE_FMRADIO_REC
1032 int prev_rec_source
= global_settings
.rec_source
; /* detect source change */
1035 struct audio_recording_options rec_options
;
1036 rec_status
= RCSTAT_IN_RECSCREEN
;
1038 #if (CONFIG_STORAGE & STORAGE_ATA) && (CONFIG_LED == LED_REAL) \
1039 && !defined(SIMULATOR)
1040 ata_set_led_enabled(false);
1043 #if CONFIG_CODEC == SWCODEC
1044 /* recording_menu gets messed up: so prevent manus talking */
1046 /* audio_init_recording stops anything playing when it takes the audio
1049 /* Yes, we use the D/A for monitoring */
1050 peak_meter_enabled
= true;
1051 peak_meter_playback(true);
1055 peak_meter_get_peakhold(&peak_l
, &peak_r
);
1058 pm_reset_clipcount();
1059 pm_activate_clipcount(false);
1060 settings_apply_trigger();
1063 agc_preset_str
[0] = str(LANG_OFF
);
1064 agc_preset_str
[1] = str(LANG_AGC_SAFETY
);
1065 agc_preset_str
[2] = str(LANG_AGC_LIVE
);
1066 agc_preset_str
[3] = str(LANG_AGC_DJSET
);
1067 agc_preset_str
[4] = str(LANG_AGC_MEDIUM
);
1068 agc_preset_str
[5] = str(LANG_AGC_VOICE
);
1069 #endif /* HAVE_AGC */
1072 /* Disable speaker to prevent feedback */
1073 audiohw_enable_speaker(false);
1076 #if CONFIG_CODEC == SWCODEC
1077 audio_close_recording();
1079 audio_init_recording(0);
1080 sound_set_volume(global_settings
.volume
);
1083 /* Create new filename for recording start */
1084 rec_init_filename();
1087 /* viewport init and calculations that only needs to be done once */
1091 /* top vp, 4 lines, force sys font if total screen < 6 lines
1092 NOTE: one could limit the list to 1 line and get away with 5 lines */
1094 viewport_set_defaults(v
, i
);
1095 if (viewport_get_nb_lines(v
) < 4)
1097 /* compact needs 4 lines total */
1098 v
->font
= FONT_SYSFIXED
;
1099 compact_view
[i
] = false;
1103 if (viewport_get_nb_lines(v
) < (4+2)) /*top=4,list=2*/
1104 compact_view
[i
] = true;
1106 compact_view
[i
] = false;
1108 vp_list
[i
] = *v
; /* get a copy now so it can be sized more easily */
1109 v
->height
= (font_get(v
->font
)->height
)*(compact_view
[i
] ? 3 : 4);
1111 /* list section, rest of the screen */
1112 vp_list
[i
].y
+= vp_top
[i
].height
;
1113 vp_list
[i
].height
-= vp_top
[i
].height
;
1114 screens
[i
].set_viewport(&vp_top
[i
]); /* req for next calls */
1116 screens
[i
].getstringsize("W", &w
, &h
);
1117 pm_y
[i
] = font_get(vp_top
[i
].font
)->height
* 2;
1118 trig_ypos
[i
] = font_get(vp_top
[i
].font
)->height
* 3;
1120 trig_ypos
[i
] -= (font_get(vp_top
[i
].font
)->height
)/2;
1123 /* init the bottom list */
1124 gui_synclist_init(&lists
, reclist_get_name
, NULL
, false, 1, vp_list
);
1125 gui_synclist_set_title(&lists
, NULL
, Icon_NOICON
);
1128 send_event(GUI_EVENT_ACTIONUPDATE
, (void*)1); /* force a redraw */
1130 /* start of the loop: we stay in this loop until user quits recscreen */
1135 /* request to re-init stuff, done after settings screen */
1137 #ifdef HAVE_FMRADIO_REC
1138 /* If input changes away from FM Radio,
1139 radio will remain off when recording screen closes. */
1140 if (global_settings
.rec_source
!= prev_rec_source
1141 && prev_rec_source
== AUDIO_SRC_FMRADIO
)
1142 radio_status
= FMRADIO_OFF
;
1143 prev_rec_source
= global_settings
.rec_source
;
1149 if(global_settings
.peak_meter_clipcounter
)
1152 screens
[i
].getstringsize(str(LANG_PM_CLIPCOUNT
),
1153 &clipwidth
, &h
); /* h is same */
1154 pm_x
[i
] = clipwidth
+1;
1156 if(global_settings
.rec_trigger_mode
== TRIG_MODE_OFF
)
1157 pm_h
[i
] = font_get(vp_top
[i
].font
)->height
* 2;
1159 pm_h
[i
] = font_get(vp_top
[i
].font
)->height
;
1162 trig_width
[i
] = vp_top
[i
].width
- pm_x
[i
];
1165 #if CONFIG_CODEC == SWCODEC
1166 audio_close_recording();
1167 audio_init_recording(0);
1170 rec_init_recording_options(&rec_options
);
1171 rec_set_recording_options(&rec_options
);
1173 if(rec_create_directory() < 0)
1179 #if CONFIG_CODEC == SWCODEC && CONFIG_RTC == 0
1180 /* If format changed, a new number is required */
1181 rec_init_filename();
1186 if (global_settings
.rec_source
== AUDIO_SRC_MIC
) {
1187 agc_preset
= global_settings
.rec_agc_preset_mic
;
1188 agc_maxgain
= global_settings
.rec_agc_maxgain_mic
;
1193 agc_preset
= global_settings
.rec_agc_preset_line
;
1194 agc_maxgain
= global_settings
.rec_agc_maxgain_line
;
1196 #endif /* HAVE_AGC */
1199 update_countdown
= 0; /* Update immediately */
1201 /* populate translation table for list id -> enum */
1202 #ifdef HAVE_SPDIF_REC
1203 if(global_settings
.rec_source
== AUDIO_SRC_SPDIF
)
1205 listid_to_enum
[0] = ITEM_VOLUME_D
;
1206 #if CONFIG_CODEC == SWCODEC
1207 listid_to_enum
[1] = ITEM_SAMPLERATE_D
;
1208 listid_to_enum
[2] = ITEM_FILENAME_D
;
1210 listid_to_enum
[1] = ITEM_FILENAME_D
;
1213 gui_synclist_set_nb_items(&lists
, ITEM_COUNT_D
); /* spdif */
1217 if(HAVE_MIC_REC_((global_settings
.rec_source
== AUDIO_SRC_MIC
) || )
1218 (global_settings
.rec_channels
== 1))
1220 listid_to_enum
[0] = ITEM_VOLUME_M
;
1221 listid_to_enum
[1] = ITEM_GAIN_M
;
1223 listid_to_enum
[2] = ITEM_AGC_MODE_M
;
1224 listid_to_enum
[3] = ITEM_AGC_MAXDB_M
;
1225 listid_to_enum
[4] = ITEM_FILENAME_M
;
1227 listid_to_enum
[2] = ITEM_FILENAME_M
;
1229 gui_synclist_set_nb_items(&lists
, ITEM_COUNT_M
); /* mono */
1233 listid_to_enum
[0] = ITEM_VOLUME
;
1234 listid_to_enum
[1] = ITEM_GAIN
;
1235 listid_to_enum
[2] = ITEM_GAIN_L
;
1236 listid_to_enum
[3] = ITEM_GAIN_R
;
1238 listid_to_enum
[4] = ITEM_AGC_MODE
;
1239 listid_to_enum
[5] = ITEM_AGC_MAXDB
;
1240 listid_to_enum
[6] = ITEM_FILENAME
;
1242 listid_to_enum
[4] = ITEM_FILENAME
;
1244 gui_synclist_set_nb_items(&lists
, ITEM_COUNT
); /* stereo */
1247 gui_synclist_draw(&lists
);
1248 } /* if(done < 0) */
1250 audio_stat
= audio_status();
1252 #if (CONFIG_LED == LED_REAL)
1255 * Flash the LED while waiting to record. Turn it on while
1258 if(audio_stat
& AUDIO_STATUS_RECORD
)
1260 if (audio_stat
& AUDIO_STATUS_PAUSE
)
1262 if (--led_countdown
<= 0)
1264 led_state
= !led_state
;
1271 /* trigger is on in status TRIG_READY (no check needed) */
1277 int trig_stat
= peak_meter_trigger_status();
1279 * other trigger stati than trig_off and trig_steady
1280 * already imply that we are recording.
1282 if (trig_stat
== TRIG_STEADY
)
1284 if (--led_countdown
<= 0)
1286 led_state
= !led_state
;
1293 /* trigger is on in status TRIG_READY (no check needed) */
1297 #endif /* CONFIG_LED */
1299 /* Wait for a button a while (HZ/10) drawing the peak meter */
1300 button
= peak_meter_draw_get_btn(CONTEXT_RECSCREEN
,
1302 screen_update
, vp_top
);
1303 if (last_audio_stat
!= audio_stat
)
1305 if (audio_stat
& AUDIO_STATUS_RECORD
)
1307 rec_status
|= RCSTAT_HAVE_RECORDED
;
1309 last_audio_stat
= audio_stat
;
1313 if (recording_start_automatic
)
1315 /* simulate a button press */
1316 button
= ACTION_REC_PAUSE
;
1317 recording_start_automatic
= false;
1320 /* let list handle the button */
1321 gui_synclist_do_button(&lists
, &button
, LIST_WRAP_UNLESS_HELD
);
1326 case ACTION_SETTINGS_INC
:
1327 case ACTION_SETTINGS_INCREPEAT
:
1328 switch (listid_to_enum
[gui_synclist_get_sel_pos(&lists
)])
1331 global_settings
.volume
++;
1336 if(global_settings
.rec_source
== AUDIO_SRC_MIC
)
1338 if(global_settings
.rec_mic_gain
<
1339 sound_max(SOUND_MIC_GAIN
))
1340 global_settings
.rec_mic_gain
++;
1345 if(global_settings
.rec_left_gain
<
1346 sound_max(SOUND_LEFT_GAIN
))
1347 global_settings
.rec_left_gain
++;
1348 if(global_settings
.rec_right_gain
<
1349 sound_max(SOUND_RIGHT_GAIN
))
1350 global_settings
.rec_right_gain
++;
1354 if(global_settings
.rec_left_gain
<
1355 sound_max(SOUND_LEFT_GAIN
))
1356 global_settings
.rec_left_gain
++;
1359 if(global_settings
.rec_right_gain
<
1360 sound_max(SOUND_RIGHT_GAIN
))
1361 global_settings
.rec_right_gain
++;
1365 agc_preset
= MIN(agc_preset
+ 1, AGC_MODE_SIZE
);
1366 agc_enable
= (agc_preset
!= 0);
1368 if (global_settings
.rec_source
== AUDIO_SRC_MIC
) {
1369 global_settings
.rec_agc_preset_mic
= agc_preset
;
1370 agc_maxgain
= global_settings
.rec_agc_maxgain_mic
;
1374 global_settings
.rec_agc_preset_line
= agc_preset
;
1375 agc_maxgain
= global_settings
.rec_agc_maxgain_line
;
1378 case ITEM_AGC_MAXDB
:
1380 if (global_settings
.rec_source
== AUDIO_SRC_MIC
)
1382 agc_maxgain
= MIN(agc_maxgain
+ 1,
1383 sound_max(SOUND_MIC_GAIN
));
1384 global_settings
.rec_agc_maxgain_mic
= agc_maxgain
;
1389 agc_maxgain
= MIN(agc_maxgain
+ 1,
1390 sound_max(SOUND_LEFT_GAIN
));
1391 global_settings
.rec_agc_maxgain_line
= agc_maxgain
;
1394 #endif /* HAVE_AGC */
1397 update_countdown
= 0; /* Update immediately */
1399 case ACTION_SETTINGS_DEC
:
1400 case ACTION_SETTINGS_DECREPEAT
:
1401 switch (listid_to_enum
[gui_synclist_get_sel_pos(&lists
)])
1404 global_settings
.volume
--;
1409 if(global_settings
.rec_source
== AUDIO_SRC_MIC
)
1411 if(global_settings
.rec_mic_gain
>
1412 sound_min(SOUND_MIC_GAIN
))
1413 global_settings
.rec_mic_gain
--;
1418 if(global_settings
.rec_left_gain
>
1419 sound_min(SOUND_LEFT_GAIN
))
1420 global_settings
.rec_left_gain
--;
1421 if(global_settings
.rec_right_gain
>
1422 sound_min(SOUND_RIGHT_GAIN
))
1423 global_settings
.rec_right_gain
--;
1427 if(global_settings
.rec_left_gain
>
1428 sound_min(SOUND_LEFT_GAIN
))
1429 global_settings
.rec_left_gain
--;
1432 if(global_settings
.rec_right_gain
>
1433 sound_min(SOUND_RIGHT_GAIN
))
1434 global_settings
.rec_right_gain
--;
1438 agc_preset
= MAX(agc_preset
- 1, 0);
1439 agc_enable
= (agc_preset
!= 0);
1441 if (global_settings
.rec_source
== AUDIO_SRC_MIC
) {
1442 global_settings
.rec_agc_preset_mic
= agc_preset
;
1443 agc_maxgain
= global_settings
.rec_agc_maxgain_mic
;
1447 global_settings
.rec_agc_preset_line
= agc_preset
;
1448 agc_maxgain
= global_settings
.rec_agc_maxgain_line
;
1451 case ITEM_AGC_MAXDB
:
1453 if (global_settings
.rec_source
== AUDIO_SRC_MIC
)
1455 agc_maxgain
= MAX(agc_maxgain
- 1,
1456 sound_min(SOUND_MIC_GAIN
));
1457 global_settings
.rec_agc_maxgain_mic
= agc_maxgain
;
1461 agc_maxgain
= MAX(agc_maxgain
- 1,
1462 sound_min(SOUND_LEFT_GAIN
));
1463 global_settings
.rec_agc_maxgain_line
= agc_maxgain
;
1466 #endif /* HAVE_AGC */
1469 update_countdown
= 0; /* Update immediately */
1471 case ACTION_STD_CANCEL
:
1472 /* turn off the trigger */
1473 peak_meter_trigger(false);
1474 peak_meter_set_trigger_listener(NULL
);
1476 if(audio_stat
& AUDIO_STATUS_RECORD
)
1478 rec_command(RECORDING_CMD_STOP
);
1482 #if CONFIG_CODEC != SWCODEC
1483 peak_meter_playback(true);
1484 peak_meter_enabled
= false;
1488 update_countdown
= 0; /* Update immediately */
1490 #ifdef HAVE_REMOTE_LCD
1491 case ACTION_REC_LCD
:
1492 /* this feature exists for some h1x0/h3x0 targets that suffer
1493 from noise caused by remote LCD updates
1494 NOTE 1: this will leave the list on the remote
1495 NOTE 2: to be replaced by a global LCD_off() routine */
1496 if(remote_display_on
)
1498 /* switch to single screen, leave message on remote */
1500 screens
[1].clear_viewport();
1501 screens
[1].puts(0, 0, str(LANG_REMOTE_LCD_OFF
));
1502 screens
[1].puts(0, 1, str(LANG_REMOTE_LCD_ON
));
1503 screens
[1].update_viewport();
1507 /* remote switched on again */
1509 screen_update
= NB_SCREENS
;
1511 remote_display_on
= !remote_display_on
; /* toggle */
1512 update_countdown
= 0; /* Update immediately */
1515 case ACTION_REC_PAUSE
:
1516 case ACTION_REC_NEWFILE
:
1517 /* Only act if the mpeg is stopped */
1518 if(!(audio_stat
& AUDIO_STATUS_RECORD
))
1520 /* is this manual or triggered recording? */
1521 if ((global_settings
.rec_trigger_mode
== TRIG_MODE_OFF
) ||
1522 (peak_meter_trigger_status() != TRIG_OFF
))
1524 /* manual recording */
1525 rec_status
|= RCSTAT_HAVE_RECORDED
;
1526 rec_command(RECORDING_CMD_START
);
1528 if (global_settings
.talk_menu
)
1530 /* no voice possible here, but a beep */
1531 audio_beep(HZ
/2); /* longer beep on start */
1534 /* this is triggered recording */
1537 /* we don't start recording now, but enable the
1538 trigger and let the callback function
1539 trigger_listener control when the recording starts */
1540 peak_meter_trigger(true);
1541 peak_meter_set_trigger_listener(&trigger_listener
);
1546 /*if new file button pressed, start new file */
1547 if (button
== ACTION_REC_NEWFILE
)
1549 rec_command(RECORDING_CMD_START_NEWFILE
);
1553 /* if pause button pressed, pause or resume */
1555 if(audio_stat
& AUDIO_STATUS_PAUSE
)
1557 rec_command(RECORDING_CMD_RESUME
);
1558 if (global_settings
.talk_menu
)
1560 /* no voice possible here, but a beep */
1561 audio_beep(HZ
/4); /* short beep on resume */
1566 rec_command(RECORDING_CMD_PAUSE
);
1570 update_countdown
= 0; /* Update immediately */
1572 case ACTION_STD_MENU
:
1573 #if CONFIG_CODEC == SWCODEC
1574 if(!(audio_stat
& AUDIO_STATUS_RECORD
))
1576 if(audio_stat
!= AUDIO_STATUS_RECORD
)
1579 #if (CONFIG_LED == LED_REAL)
1580 /* led is restored at begin of loop / end of function */
1583 if (recording_menu(no_source
))
1586 rec_status
|= RCSTAT_BEEN_IN_USB_MODE
;
1587 #ifdef HAVE_FMRADIO_REC
1588 radio_status
= FMRADIO_OFF
;
1594 /* the init is now done at the beginning of the loop */
1599 #if CONFIG_KEYPAD == RECORDER_PAD
1601 if(audio_stat
!= AUDIO_STATUS_RECORD
)
1603 #if (CONFIG_LED == LED_REAL)
1604 /* led is restored at begin of loop / end of function */
1607 if (f2_rec_screen())
1609 rec_status
|= RCSTAT_HAVE_RECORDED
;
1613 update_countdown
= 0; /* Update immediately */
1618 if(audio_stat
& AUDIO_STATUS_RECORD
)
1620 rec_command(RECORDING_CMD_START_NEWFILE
);
1625 #if (CONFIG_LED == LED_REAL)
1626 /* led is restored at begin of loop / end of function */
1629 if (f3_rec_screen())
1631 rec_status
|= RCSTAT_HAVE_RECORDED
;
1635 update_countdown
= 0; /* Update immediately */
1638 #endif /* CONFIG_KEYPAD == RECORDER_PAD */
1640 case SYS_USB_CONNECTED
:
1641 /* Only accept USB connection when not recording */
1642 if(!(audio_stat
& AUDIO_STATUS_RECORD
))
1645 screens
[i
].set_viewport(NULL
);
1646 default_event_handler(SYS_USB_CONNECTED
);
1648 rec_status
|= RCSTAT_BEEN_IN_USB_MODE
;
1649 #ifdef HAVE_FMRADIO_REC
1650 radio_status
= FMRADIO_OFF
;
1654 } /*switch(button)*/
1657 peak_read
= !peak_read
;
1658 if (peak_read
) { /* every 2nd run of loop */
1660 peak_valid
= read_peak_levels(&peak_l
, &peak_r
, &balance
);
1663 /* Handle AGC every 200ms when enabled and peak data is valid */
1664 if (peak_read
&& agc_enable
&& peak_valid
)
1665 auto_gain_control(&peak_l
, &peak_r
, &balance
);
1668 seconds
= audio_recorded_time() / HZ
;
1670 /* start of vp_top drawing */
1671 if(update_countdown
-- == 0 || seconds
> last_seconds
)
1673 unsigned int dseconds
, dhours
, dminutes
;
1674 unsigned long num_recorded_bytes
, dsize
, dmb
;
1679 screens
[i
].set_viewport(&vp_top
[i
]);
1680 screens
[i
].clear_viewport();
1682 update_countdown
= 5;
1683 last_seconds
= seconds
;
1685 dseconds
= rec_timesplit_seconds();
1686 dsize
= rec_sizesplit_bytes();
1687 num_recorded_bytes
= audio_num_recorded_bytes();
1689 #if CONFIG_CODEC == SWCODEC
1690 if ((audio_stat
& AUDIO_STATUS_WARNING
)
1691 && (warning_counter
++ % WARNING_PERIOD
) < WARNING_PERIOD
/2)
1693 /* Switch back and forth displaying warning on first available
1694 line to ensure visibility - the motion should also help
1696 /* Don't use language string unless agreed upon to make this
1697 method permanent - could do something in the statusbar */
1698 snprintf(buf
, sizeof(buf
), "Warning: %08X",
1699 pcm_rec_get_warnings());
1702 #endif /* CONFIG_CODEC == SWCODEC */
1703 if ((global_settings
.rec_sizesplit
) &&
1704 (global_settings
.rec_split_method
))
1706 dmb
= dsize
/1024/1024;
1707 snprintf(buf
, sizeof(buf
), "%s %dMB",
1708 str(LANG_SPLIT_SIZE
), dmb
);
1712 hours
= seconds
/ 3600;
1713 minutes
= (seconds
- (hours
* 3600)) / 60;
1714 snprintf(buf
, sizeof(buf
), "%s %02d:%02d:%02d",
1715 str(LANG_RECORDING_TIME
),
1716 hours
, minutes
, seconds
%60);
1719 FOR_NB_ACTIVE_SCREENS(i
)
1720 screens
[i
].puts(0, 0, buf
);
1722 if(audio_stat
& AUDIO_STATUS_PRERECORD
)
1724 snprintf(buf
, sizeof(buf
), "%s...",
1725 str(LANG_RECORD_PRERECORD
));
1729 /* Display the split interval if the record timesplit
1731 if ((global_settings
.rec_timesplit
) &&
1732 !(global_settings
.rec_split_method
))
1734 /* Display the record timesplit interval rather
1735 than the file size if the record timer is active */
1736 dhours
= dseconds
/ 3600;
1737 dminutes
= (dseconds
- (dhours
* 3600)) / 60;
1738 snprintf(buf
, sizeof(buf
), "%s %02d:%02d",
1739 str(LANG_RECORDING_TIMESPLIT_REC
),
1744 output_dyn_value(buf2
, sizeof buf2
,
1747 snprintf(buf
, sizeof(buf
), "%s %s",
1748 str(LANG_RECORDING_SIZE
), buf2
);
1752 FOR_NB_ACTIVE_SCREENS(i
)
1753 screens
[i
].puts(0, 1, buf
);
1755 /* We will do file splitting regardless, either at the end of
1756 a split interval, or when the filesize approaches the 2GB
1757 FAT file size (compatibility) limit. */
1758 if ((audio_stat
&& !(global_settings
.rec_split_method
)
1759 && global_settings
.rec_timesplit
&& (seconds
>= dseconds
))
1760 || (audio_stat
&& global_settings
.rec_split_method
1761 && global_settings
.rec_sizesplit
1762 && (num_recorded_bytes
>= dsize
))
1763 || (num_recorded_bytes
>= MAX_FILE_SIZE
))
1765 if (!(global_settings
.rec_split_type
)
1766 || (num_recorded_bytes
>= MAX_FILE_SIZE
))
1768 rec_command(RECORDING_CMD_START_NEWFILE
);
1773 peak_meter_trigger(false);
1774 peak_meter_set_trigger_listener(NULL
);
1775 if( global_settings
.rec_split_type
== 1)
1776 rec_command(RECORDING_CMD_STOP
);
1778 rec_command(RECORDING_CMD_STOP_SHUTDOWN
);
1780 update_countdown
= 0;
1783 /* draw the clipcounter just in front of the peakmeter */
1784 if(global_settings
.peak_meter_clipcounter
)
1786 int clipcount
= pm_get_clipcount();
1787 FOR_NB_ACTIVE_SCREENS(i
)
1789 if(!compact_view
[i
])
1791 screens
[i
].puts(0, 2, str(LANG_PM_CLIPCOUNT
));
1792 screens
[i
].putsf(0, 3, "%4d", clipcount
);
1795 screens
[i
].putsf(0, 2, "%4d", clipcount
);
1803 /* draw the trigger status */
1804 if (peak_meter_trigger_status() != TRIG_OFF
)
1806 peak_meter_draw_trig(pm_x
, trig_ypos
, trig_width
,
1808 FOR_NB_ACTIVE_SCREENS(i
)
1809 screens
[i
].update_viewport_rect(pm_x
[i
], trig_ypos
[i
],
1810 trig_width
[i
] + 2, TRIG_HEIGHT
);
1815 if (global_settings
.rec_source
== AUDIO_SRC_MIC
)
1817 if(agc_maxgain
< (global_settings
.rec_mic_gain
))
1818 change_recording_gain(false, true, true);
1823 if(agc_maxgain
< (global_settings
.rec_left_gain
))
1824 change_recording_gain(false, true, false);
1825 if(agc_maxgain
< (global_settings
.rec_right_gain
))
1826 change_recording_gain(false, false, true);
1828 #endif /* HAVE_AGC */
1830 #if CONFIG_CODEC == SWCODEC
1831 #ifdef HAVE_SPDIF_REC
1832 if((global_settings
.rec_source
== AUDIO_SRC_SPDIF
) &&
1833 (prev_sample_rate
!= pcm_rec_sample_rate()))
1835 /* spdif samplerate changed */
1836 prev_sample_rate
= pcm_rec_sample_rate();
1844 /* update_list is set whenever content changes */
1845 update_list
= false;
1846 gui_synclist_draw(&lists
);
1849 /* draw peakmeter again (check if this can be removed) */
1850 FOR_NB_ACTIVE_SCREENS(i
)
1852 screens
[i
].set_viewport(&vp_top
[i
]);
1853 peak_meter_screen(&screens
[i
], pm_x
[i
], pm_y
[i
], pm_h
[i
]);
1854 screens
[i
].update();
1856 } /* display update every second */
1858 if(audio_stat
& AUDIO_STATUS_ERROR
)
1862 } /* end while(!done) */
1864 audio_stat
= audio_status();
1865 if (audio_stat
& AUDIO_STATUS_ERROR
)
1867 splash(0, str(LANG_DISK_FULL
));
1870 screens
[i
].update();
1872 #if CONFIG_CODEC == SWCODEC
1873 /* stop recording - some players like H10 freeze otherwise
1874 TO DO: find out why it freezes and fix properly */
1875 rec_command(RECORDING_CMD_STOP
);
1876 audio_close_recording();
1879 audio_error_clear();
1883 if (action_userabort(TIMEOUT_NOBLOCK
))
1890 #if CONFIG_CODEC == SWCODEC
1891 rec_command(RECORDING_CMD_STOP
);
1892 audio_close_recording();
1894 #ifdef HAVE_FMRADIO_REC
1895 if (radio_status
!= FMRADIO_OFF
)
1896 /* Restore radio playback - radio_status should be unchanged if started
1897 through fm radio screen (barring usb connect) */
1898 rec_set_source(AUDIO_SRC_FMRADIO
, (radio_status
& FMRADIO_PAUSED
) ?
1899 SRCF_FMRADIO_PAUSED
: SRCF_FMRADIO_PLAYING
);
1902 /* Go back to playback mode */
1903 rec_set_source(AUDIO_SRC_PLAYBACK
, SRCF_PLAYBACK
);
1905 /* restore talking */
1906 talk_disable(false);
1907 #else /* !SWCODEC */
1908 audio_init_playback();
1909 #endif /* CONFIG_CODEC == SWCODEC */
1912 /* Re-enable speaker */
1913 audiohw_enable_speaker(global_settings
.speaker_enabled
);
1916 /* make sure the trigger is really turned off */
1917 peak_meter_trigger(false);
1918 peak_meter_set_trigger_listener(NULL
);
1920 rec_status
&= ~RCSTAT_IN_RECSCREEN
;
1921 sound_settings_apply();
1924 screens
[i
].setfont(FONT_UI
);
1927 /* if the directory was created or recording happened, make sure the
1928 browser is updated */
1929 if (rec_status
& (RCSTAT_CREATED_DIRECTORY
| RCSTAT_HAVE_RECORDED
))
1932 #if (CONFIG_STORAGE & STORAGE_ATA) && (CONFIG_LED == LED_REAL) \
1933 && !defined(SIMULATOR)
1934 ata_set_led_enabled(true);
1939 return (rec_status
& RCSTAT_BEEN_IN_USB_MODE
) != 0;
1940 } /* recording_screen */
1942 #if CONFIG_KEYPAD == RECORDER_PAD
1943 static bool f2_rec_screen(void)
1945 static const char* const freq_str
[6] =
1960 struct audio_recording_options rec_options
;
1964 screens
[i
].setfont(FONT_SYSFIXED
);
1965 screens
[i
].getstringsize("A",&w
,&h
);
1973 screens
[i
].clear_display();
1975 /* Recording quality */
1976 screens
[i
].putsxy(0, LCD_HEIGHT
/2 - h
*2,
1977 str(LANG_SYSFONT_RECORDING_QUALITY
));
1980 snprintf(buf
, sizeof(buf
), "%d", global_settings
.rec_quality
);
1983 screens
[i
].putsxy(0, LCD_HEIGHT
/2-h
, buf
);
1984 screens
[i
].mono_bitmap(bitmap_icons_7x8
[Icon_FastBackward
],
1985 LCD_WIDTH
/2 - 16, LCD_HEIGHT
/2 - 4, 7, 8);
1989 snprintf(buf
, sizeof buf
, "%s:", str(LANG_SYSFONT_RECORDING_FREQUENCY
));
1990 ptr
= freq_str
[global_settings
.rec_frequency
];
1993 screens
[i
].getstringsize(buf
,&w
,&h
);
1994 screens
[i
].putsxy((LCD_WIDTH
-w
)/2, LCD_HEIGHT
- h
*2, buf
);
1995 screens
[i
].getstringsize(ptr
, &w
, &h
);
1996 screens
[i
].putsxy((LCD_WIDTH
-w
)/2, LCD_HEIGHT
- h
, ptr
);
1997 screens
[i
].mono_bitmap(bitmap_icons_7x8
[Icon_DownArrow
],
1998 LCD_WIDTH
/2 - 3, LCD_HEIGHT
- h
*3, 7, 8);
2002 switch ( global_settings
.rec_channels
) {
2004 ptr
= str(LANG_SYSFONT_CHANNEL_STEREO
);
2008 ptr
= str(LANG_SYSFONT_CHANNEL_MONO
);
2014 screens
[i
].getstringsize(str(LANG_SYSFONT_CHANNELS
), &w
, &h
);
2015 screens
[i
].putsxy(LCD_WIDTH
- w
, LCD_HEIGHT
/2 - h
*2,
2016 str(LANG_SYSFONT_CHANNELS
));
2017 screens
[i
].getstringsize(str(LANG_SYSFONT_MODE
), &w
, &h
);
2018 screens
[i
].putsxy(LCD_WIDTH
- w
, LCD_HEIGHT
/2 - h
,
2019 str(LANG_SYSFONT_MODE
));
2020 screens
[i
].getstringsize(ptr
, &w
, &h
);
2021 screens
[i
].putsxy(LCD_WIDTH
- w
, LCD_HEIGHT
/2, ptr
);
2022 screens
[i
].mono_bitmap(bitmap_icons_7x8
[Icon_FastForward
],
2023 LCD_WIDTH
/2 + 8, LCD_HEIGHT
/2 - 4, 7, 8);
2025 screens
[i
].update();
2028 button
= button_get(true);
2031 case BUTTON_F2
| BUTTON_LEFT
:
2032 global_settings
.rec_quality
++;
2033 if(global_settings
.rec_quality
> 7)
2034 global_settings
.rec_quality
= 0;
2039 case BUTTON_F2
| BUTTON_DOWN
:
2040 global_settings
.rec_frequency
++;
2041 if(global_settings
.rec_frequency
> 5)
2042 global_settings
.rec_frequency
= 0;
2047 case BUTTON_F2
| BUTTON_RIGHT
:
2048 global_settings
.rec_channels
++;
2049 if(global_settings
.rec_channels
> 1)
2050 global_settings
.rec_channels
= 0;
2054 case BUTTON_F2
| BUTTON_REL
:
2060 case BUTTON_F2
| BUTTON_REPEAT
:
2065 if(default_event_handler(button
) == SYS_USB_CONNECTED
)
2071 rec_init_recording_options(&rec_options
);
2072 rec_set_recording_options(&rec_options
);
2078 screens
[i
].setfont(FONT_UI
);
2083 static bool f3_rec_screen(void)
2089 const char *src_str
[] =
2091 str(LANG_SYSFONT_RECORDING_SRC_MIC
),
2092 str(LANG_SYSFONT_LINE_IN
),
2093 str(LANG_SYSFONT_RECORDING_SRC_DIGITAL
)
2095 struct audio_recording_options rec_options
;
2099 screens
[i
].setfont(FONT_SYSFIXED
);
2100 screens
[i
].getstringsize("A",&w
,&h
);
2104 const char* ptr
= src_str
[global_settings
.rec_source
];
2107 screens
[i
].clear_display();
2109 /* Recording source */
2110 screens
[i
].putsxy(0, LCD_HEIGHT
/2 - h
*2,
2111 str(LANG_SYSFONT_RECORDING_SOURCE
));
2113 screens
[i
].getstringsize(ptr
, &w
, &h
);
2114 screens
[i
].putsxy(0, LCD_HEIGHT
/2-h
, ptr
);
2115 screens
[i
].mono_bitmap(bitmap_icons_7x8
[Icon_FastBackward
],
2116 LCD_WIDTH
/2 - 16, LCD_HEIGHT
/2 - 4, 7, 8);
2120 ptr
= str(LANG_SYSFONT_RECORD_TRIGGER
);
2123 screens
[i
].getstringsize(ptr
,&w
,&h
);
2124 screens
[i
].putsxy((LCD_WIDTH
-w
)/2, LCD_HEIGHT
- h
*2, ptr
);
2125 screens
[i
].mono_bitmap(bitmap_icons_7x8
[Icon_DownArrow
],
2126 LCD_WIDTH
/2 - 3, LCD_HEIGHT
- h
*3, 7, 8);
2128 screens
[i
].update();
2131 button
= button_get(true);
2134 case BUTTON_F3
| BUTTON_DOWN
:
2137 settings_apply_trigger();
2143 case BUTTON_F3
| BUTTON_LEFT
:
2144 global_settings
.rec_source
++;
2145 if(global_settings
.rec_source
> AUDIO_SRC_MAX
)
2146 global_settings
.rec_source
= 0;
2150 case BUTTON_F3
| BUTTON_REL
:
2156 case BUTTON_F3
| BUTTON_REPEAT
:
2161 if(default_event_handler(button
) == SYS_USB_CONNECTED
)
2167 rec_init_recording_options(&rec_options
);
2168 rec_set_recording_options(&rec_options
);
2174 screens
[i
].setfont(FONT_UI
);
2178 #endif /* CONFIG_KEYPAD == RECORDER_PAD */
2180 #if CONFIG_CODEC == SWCODEC
2181 void audio_beep(int duration
)
2188 /* stubs for recording sim */
2189 void audio_init_recording(unsigned int buffer_offset
)
2191 buffer_offset
= buffer_offset
;
2194 void audio_close_recording(void)
2198 unsigned long pcm_rec_get_warnings(void)
2203 unsigned long pcm_rec_sample_rate(void)
2208 unsigned long audio_recorded_time(void)
2213 unsigned long audio_num_recorded_bytes(void)
2215 return 5 * 1024 * 1024;
2218 void rec_set_source(int source
, unsigned flags
)
2224 void audio_set_recording_options(struct audio_recording_options
*options
)
2229 void audio_set_recording_gain(int left
, int right
, int type
)
2236 void audio_record(const char *filename
)
2238 filename
= filename
;
2241 void audio_new_file(const char *filename
)
2243 filename
= filename
;
2246 void audio_stop_recording(void)
2250 void audio_pause_recording(void)
2254 void audio_resume_recording(void)
2258 #endif /* #ifdef SIMULATOR */
2259 #endif /* #ifdef CONFIG_CODEC == SWCODEC */
2261 #endif /* HAVE_RECORDING */