Increase MAXTHREADS
[Rockbox.git] / apps / recorder / recording.c
blob34c2c6c60544f8ac0ea34dfd7dc28ba5909a31e5
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "config.h"
22 #include <stdio.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
26 #include "system.h"
27 #include "power.h"
28 #include "lcd.h"
29 #include "led.h"
30 #include "mpeg.h"
31 #include "audio.h"
32 #if CONFIG_CODEC == SWCODEC
33 #include "thread.h"
34 #include "playback.h"
35 #include "enc_config.h"
36 #if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT)
37 #include "spdif.h"
38 #endif
39 #endif /* CONFIG_CODEC == SWCODEC */
40 #include "recording.h"
41 #include "mp3_playback.h"
42 #include "mas.h"
43 #include "button.h"
44 #include "kernel.h"
45 #include "settings.h"
46 #include "lang.h"
47 #include "font.h"
48 #include "icons.h"
49 #include "icon.h"
50 #include "screens.h"
51 #include "peakmeter.h"
52 #include "statusbar.h"
53 #include "menu.h"
54 #include "sound_menu.h"
55 #include "timefuncs.h"
56 #include "debug.h"
57 #include "misc.h"
58 #include "tree.h"
59 #include "string.h"
60 #include "dir.h"
61 #include "errno.h"
62 #include "talk.h"
63 #include "atoi.h"
64 #include "sound.h"
65 #include "ata.h"
66 #include "splash.h"
67 #include "screen_access.h"
68 #include "action.h"
69 #include "radio.h"
70 #ifdef HAVE_RECORDING
71 /* This array holds the record timer interval lengths, in seconds */
72 static const unsigned long rec_timer_seconds[] =
74 0, /* 0 means OFF */
75 5*60, /* 00:05 */
76 10*60, /* 00:10 */
77 15*60, /* 00:15 */
78 30*60, /* 00:30 */
79 60*60, /* 01:00 */
80 74*60, /* 74:00 */
81 80*60, /* 80:00 */
82 2*60*60, /* 02:00 */
83 4*60*60, /* 04:00 */
84 6*60*60, /* 06:00 */
85 8*60*60, /* 08:00 */
86 10L*60*60, /* 10:00 */
87 12L*60*60, /* 12:00 */
88 18L*60*60, /* 18:00 */
89 24L*60*60 /* 24:00 */
92 static unsigned int rec_timesplit_seconds(void)
94 return rec_timer_seconds[global_settings.rec_timesplit];
97 /* This array holds the record size interval lengths, in bytes */
98 static const unsigned long rec_size_bytes[] =
100 0, /* 0 means OFF */
101 5*1024*1024, /* 5MB */
102 10*1024*1024, /* 10MB */
103 15*1024*1024, /* 15MB */
104 32*1024*1024, /* 32MB */
105 64*1024*1024, /* 64MB */
106 75*1024*1024, /* 75MB */
107 100*1024*1024, /* 100MB */
108 128*1024*1024, /* 128MB */
109 256*1024*1024, /* 256MB */
110 512*1024*1024, /* 512MB */
111 650*1024*1024, /* 650MB */
112 700*1024*1024, /* 700MB */
113 1024*1024*1024, /* 1GB */
114 1536*1024*1024, /* 1.5GB */
115 1792*1024*1024, /* 1.75GB */
118 static unsigned long rec_sizesplit_bytes(void)
120 return rec_size_bytes[global_settings.rec_sizesplit];
123 * Time strings used for the trigger durations.
124 * Keep synchronous to trigger_times in settings_apply_trigger
126 const struct opt_items trig_durations[TRIG_DURATION_COUNT] =
128 #define TS(x) { (unsigned char *)(#x "s"), TALK_ID(x, UNIT_SEC) }
129 #define TM(x) { (unsigned char *)(#x "min"), TALK_ID(x, UNIT_MIN) }
130 TS(0), TS(1), TS(2), TS(5),
131 TS(10), TS(15), TS(20), TS(25), TS(30),
132 TM(1), TM(2), TM(5), TM(10)
133 #undef TS
134 #undef TM
137 void settings_apply_trigger(void)
139 /* Keep synchronous to trig_durations and trig_durations_conf*/
140 static const long trigger_times[TRIG_DURATION_COUNT] = {
141 0, HZ, 2*HZ, 5*HZ,
142 10*HZ, 15*HZ, 20*HZ, 25*HZ, 30*HZ,
143 60*HZ, 2*60*HZ, 5*60*HZ, 10*60*HZ
146 peak_meter_define_trigger(
147 global_settings.rec_start_thres,
148 trigger_times[global_settings.rec_start_duration],
149 MIN(trigger_times[global_settings.rec_start_duration] / 2, 2*HZ),
150 global_settings.rec_stop_thres,
151 trigger_times[global_settings.rec_stop_postrec],
152 trigger_times[global_settings.rec_stop_gap]
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 #define PM_HEIGHT ((LCD_HEIGHT >= 72) ? 2 : 1)
173 #if CONFIG_KEYPAD == RECORDER_PAD
174 static bool f2_rec_screen(void);
175 static bool f3_rec_screen(void);
176 #endif
178 #define MAX_FILE_SIZE 0x7F800000 /* 2 GB - 4 MB */
180 static int screen_update = NB_SCREENS;
181 #ifdef HAVE_REMOTE_LCD
182 static bool remote_display_on = true;
183 #endif
185 /** File name creation **/
186 #if CONFIG_RTC == 0
187 /* current file number to assist in creating unique numbered filenames
188 without actually having to create the file on disk */
189 static int file_number = -1;
190 #endif /* CONFIG_RTC */
192 #if CONFIG_CODEC == SWCODEC
194 #define REC_FILE_ENDING(rec_format) \
195 (audio_formats[rec_format_afmt[rec_format]].ext_list)
197 #else /* CONFIG_CODEC != SWCODEC */
199 /* default record file extension for HWCODEC */
200 #define REC_FILE_ENDING(rec_format) \
201 (audio_formats[AFMT_MPA_L3].ext_list)
203 #endif /* CONFIG_CODEC == SWCODEC */
205 /* path for current file */
206 static char path_buffer[MAX_PATH];
208 /** Automatic Gain Control (AGC) **/
209 #ifdef HAVE_AGC
210 /* Timing counters:
211 * peak_time is incremented every 0.2s, every 2nd run of record screen loop.
212 * hist_time is incremented every 0.5s, display update.
213 * peak_time is the counter of the peak hold read and agc process,
214 * overflow every 13 years 8-)
216 static long peak_time = 0;
217 static long hist_time = 0;
219 static short peak_valid_mem[4];
220 #define BAL_MEM_SIZE 24
221 static short balance_mem[BAL_MEM_SIZE];
223 /* Automatic Gain Control */
224 #define AGC_MODE_SIZE 5
225 #define AGC_SAFETY_MODE 0
227 static const char* agc_preset_str[] =
228 { "Off", "S", "L", "D", "M", "V" };
229 /* "Off",
230 "Safety (clip)",
231 "Live (slow)",
232 "DJ-Set (slow)",
233 "Medium",
234 "Voice (fast)" */
235 #define AGC_CLIP 32766
236 #define AGC_PEAK 29883 /* fast gain reduction threshold -0.8dB */
237 #define AGC_HIGH 27254 /* accelerated gain reduction threshold -1.6dB */
238 #define AGC_IMG 823 /* threshold for balance control -32dB */
239 /* autogain high level thresholds (-3dB, -7dB, -4dB, -5dB, -5dB) */
240 static const short agc_th_hi[AGC_MODE_SIZE] =
241 { 23197, 14637, 21156, 18428, 18426 };
242 /* autogain low level thresholds (-14dB, -11dB, -6dB, -7dB, -8dB) */
243 static const short agc_th_lo[AGC_MODE_SIZE] =
244 { 6538, 9235, 16422, 14636, 13045 };
245 /* autogain threshold times [1/5s] or [200ms] */
246 static const short agc_tdrop[AGC_MODE_SIZE] =
247 { 900, 225, 150, 60, 8 };
248 static const short agc_trise[AGC_MODE_SIZE] =
249 { 9000, 750, 400, 150, 20 };
250 static const short agc_tbal[AGC_MODE_SIZE] =
251 { 4500, 500, 300, 100, 15 };
252 /* AGC operation */
253 static bool agc_enable = true;
254 static short agc_preset;
255 /* AGC levels */
256 static int agc_left = 0;
257 static int agc_right = 0;
258 /* AGC time since high target volume was exceeded */
259 static short agc_droptime = 0;
260 /* AGC time since volume fallen below low target */
261 static short agc_risetime = 0;
262 /* AGC balance time exceeding +/- 0.7dB */
263 static short agc_baltime = 0;
264 /* AGC maximum gain */
265 static short agc_maxgain;
266 #endif /* HAVE_AGC */
268 static void set_gain(void)
270 if(global_settings.rec_source == AUDIO_SRC_MIC)
272 audio_set_recording_gain(global_settings.rec_mic_gain,
273 0, AUDIO_GAIN_MIC);
275 else
277 /* AUDIO_SRC_LINEIN, AUDIO_SRC_FMRADIO, AUDIO_SRC_SPDIF */
278 audio_set_recording_gain(global_settings.rec_left_gain,
279 global_settings.rec_right_gain,
280 AUDIO_GAIN_LINEIN);
284 #ifdef HAVE_AGC
285 /* Read peak meter values & calculate balance.
286 * Returns validity of peak values.
287 * Used for automatic gain control and history diagram.
289 static bool read_peak_levels(int *peak_l, int *peak_r, int *balance)
291 peak_meter_get_peakhold(peak_l, peak_r);
292 peak_valid_mem[peak_time % 3] = *peak_l;
293 if (((peak_valid_mem[0] == peak_valid_mem[1]) &&
294 (peak_valid_mem[1] == peak_valid_mem[2])) &&
295 ((*peak_l < 32767)
296 #ifndef SIMULATOR
297 || ata_disk_is_active()
298 #endif
300 return false;
302 if (*peak_r > *peak_l)
303 balance_mem[peak_time % BAL_MEM_SIZE] = (*peak_l ?
304 MIN((10000 * *peak_r) / *peak_l - 10000, 15118) : 15118);
305 else
306 balance_mem[peak_time % BAL_MEM_SIZE] = (*peak_r ?
307 MAX(10000 - (10000 * *peak_l) / *peak_r, -15118) : -15118);
308 *balance = 0;
309 int i;
310 for (i = 0; i < BAL_MEM_SIZE; i++)
311 *balance += balance_mem[i];
312 *balance = *balance / BAL_MEM_SIZE;
314 return true;
317 /* AGC helper function to check if maximum gain is reached */
318 static bool agc_gain_is_max(bool left, bool right)
320 /* range -128...+108 [0.5dB] */
321 short gain_current_l;
322 short gain_current_r;
324 if (agc_preset == 0)
325 return false;
327 switch (global_settings.rec_source)
329 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
330 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
331 gain_current_l = global_settings.rec_left_gain;
332 gain_current_r = global_settings.rec_right_gain;
333 break;
334 case AUDIO_SRC_MIC:
335 default:
336 gain_current_l = global_settings.rec_mic_gain;
337 gain_current_r = global_settings.rec_mic_gain;
340 return ((left && (gain_current_l >= agc_maxgain)) ||
341 (right && (gain_current_r >= agc_maxgain)));
344 static void change_recording_gain(bool increment, bool left, bool right)
346 int factor = (increment ? 1 : -1);
348 switch (global_settings.rec_source)
350 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
351 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
352 if (left) global_settings.rec_left_gain += factor;
353 if (right) global_settings.rec_right_gain += factor;
354 break;
355 case AUDIO_SRC_MIC:
356 global_settings.rec_mic_gain += factor;
361 * Handle automatic gain control (AGC).
362 * Change recording gain if peak_x levels are above or below
363 * target volume for specified timeouts.
365 static void auto_gain_control(int *peak_l, int *peak_r, int *balance)
367 int agc_mono;
368 short agc_mode;
369 bool increment;
371 if (*peak_l > agc_left)
372 agc_left = *peak_l;
373 else
374 agc_left -= (agc_left - *peak_l + 3) >> 2;
375 if (*peak_r > agc_right)
376 agc_right = *peak_r;
377 else
378 agc_right -= (agc_right - *peak_r + 3) >> 2;
379 agc_mono = (agc_left + agc_right) / 2;
381 agc_mode = abs(agc_preset) - 1;
382 if (agc_mode < 0) {
383 agc_enable = false;
384 return;
387 if (agc_mode != AGC_SAFETY_MODE) {
388 /* Automatic balance control - only if not in safety mode */
389 if ((agc_left > AGC_IMG) && (agc_right > AGC_IMG))
391 if (*balance < -556)
393 if (*balance > -900)
394 agc_baltime -= !(peak_time % 4); /* 0.47 - 0.75dB */
395 else if (*balance > -4125)
396 agc_baltime--; /* 0.75 - 3.00dB */
397 else if (*balance > -7579)
398 agc_baltime -= 2; /* 3.00 - 4.90dB */
399 else
400 agc_baltime -= !(peak_time % 8); /* 4.90 - inf dB */
401 if (agc_baltime > 0)
402 agc_baltime -= (peak_time % 2);
404 else if (*balance > 556)
406 if (*balance < 900)
407 agc_baltime += !(peak_time % 4);
408 else if (*balance < 4125)
409 agc_baltime++;
410 else if (*balance < 7579)
411 agc_baltime += 2;
412 else
413 agc_baltime += !(peak_time % 8);
414 if (agc_baltime < 0)
415 agc_baltime += (peak_time % 2);
418 if ((*balance * agc_baltime) < 0)
420 if (*balance < 0)
421 agc_baltime -= peak_time % 2;
422 else
423 agc_baltime += peak_time % 2;
426 increment = ((agc_risetime / 2) > agc_droptime);
428 if (agc_baltime < -agc_tbal[agc_mode])
430 if (!increment || !agc_gain_is_max(!increment, increment)) {
431 change_recording_gain(increment, !increment, increment);
432 set_gain();
434 agc_baltime = 0;
436 else if (agc_baltime > +agc_tbal[agc_mode])
438 if (!increment || !agc_gain_is_max(increment, !increment)) {
439 change_recording_gain(increment, increment, !increment);
440 set_gain();
442 agc_baltime = 0;
445 else if (!(hist_time % 4))
447 if (agc_baltime < 0)
448 agc_baltime++;
449 else
450 agc_baltime--;
454 /* Automatic gain control */
455 if ((agc_left > agc_th_hi[agc_mode]) || (agc_right > agc_th_hi[agc_mode]))
457 if ((agc_left > AGC_CLIP) || (agc_right > AGC_CLIP))
458 agc_droptime += agc_tdrop[agc_mode] /
459 (global_settings.rec_agc_cliptime + 1);
460 if (agc_left > AGC_HIGH) {
461 agc_droptime++;
462 agc_risetime=0;
463 if (agc_left > AGC_PEAK)
464 agc_droptime += 2;
466 if (agc_right > AGC_HIGH) {
467 agc_droptime++;
468 agc_risetime=0;
469 if (agc_right > AGC_PEAK)
470 agc_droptime += 2;
472 if (agc_mono > agc_th_hi[agc_mode])
473 agc_droptime++;
474 else
475 agc_droptime += !(peak_time % 2);
477 if (agc_droptime >= agc_tdrop[agc_mode])
479 change_recording_gain(false, true, true);
480 agc_droptime = 0;
481 agc_risetime = 0;
482 set_gain();
484 agc_risetime = MAX(agc_risetime - 1, 0);
486 else if (agc_mono < agc_th_lo[agc_mode])
488 if (agc_mono < (agc_th_lo[agc_mode] / 8))
489 agc_risetime += !(peak_time % 5);
490 else if (agc_mono < (agc_th_lo[agc_mode] / 2))
491 agc_risetime += 2;
492 else
493 agc_risetime++;
495 if (agc_risetime >= agc_trise[agc_mode]) {
496 if ((agc_mode != AGC_SAFETY_MODE) &&
497 (!agc_gain_is_max(true, true))) {
498 change_recording_gain(true, true, true);
499 set_gain();
501 agc_risetime = 0;
502 agc_droptime = 0;
504 agc_droptime = MAX(agc_droptime - 1, 0);
506 else if (!(peak_time % 6)) /* on target level every 1.2 sec */
508 agc_risetime = MAX(agc_risetime - 1, 0);
509 agc_droptime = MAX(agc_droptime - 1, 0);
512 #endif /* HAVE_AGC */
514 static const char* const fmtstr[] =
516 "%c%d %s", /* no decimals */
517 "%c%d.%d %s ", /* 1 decimal */
518 "%c%d.%02d %s " /* 2 decimals */
521 static char *fmt_gain(int snd, int val, char *str, int len)
523 int i, d, numdec;
524 const char *unit;
525 char sign = ' ';
527 val = sound_val2phys(snd, val);
528 if(val < 0)
530 sign = '-';
531 val = -val;
533 numdec = sound_numdecimals(snd);
534 unit = sound_unit(snd);
536 if(numdec)
538 i = val / (10*numdec);
539 d = val % (10*numdec);
540 snprintf(str, len, fmtstr[numdec], sign, i, d, unit);
542 else
543 snprintf(str, len, fmtstr[numdec], sign, val, unit);
545 return str;
548 static int cursor;
550 static void adjust_cursor(void)
552 int max_cursor;
554 if(cursor < 0)
555 cursor = 0;
557 #ifdef HAVE_AGC
558 switch(global_settings.rec_source)
560 case REC_SRC_MIC:
561 if(cursor == 2)
562 cursor = 4;
563 else if(cursor == 3)
564 cursor = 1;
565 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
566 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
567 max_cursor = 5;
568 break;
569 default:
570 max_cursor = 0;
571 break;
573 #else /* !HAVE_AGC */
574 switch(global_settings.rec_source)
576 case AUDIO_SRC_MIC:
577 max_cursor = 1;
578 break;
579 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
580 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
581 max_cursor = 3;
582 break;
583 default:
584 max_cursor = 0;
585 break;
587 #endif /* HAVE_AGC */
589 if(cursor > max_cursor)
590 cursor = max_cursor;
593 static bool check_dir(char *folder)
595 DIR *dir = opendir(folder);
596 if (!dir && strcmp(folder, "/"))
598 int rc = mkdir(folder);
599 if(rc < 0)
600 return false;
601 return true;
603 closedir(dir);
604 return true;
607 /* the list below must match enum audio_sources in audio.h */
608 static const char* const prestr[] =
610 HAVE_MIC_IN_([AUDIO_SRC_MIC] = "R_MIC_",)
611 HAVE_LINE_REC_([AUDIO_SRC_LINEIN] = "R_LINE_",)
612 HAVE_SPDIF_IN_([AUDIO_SRC_SPDIF] = "R_SPDIF_",)
613 HAVE_FMRADIO_REC_([AUDIO_SRC_FMRADIO] = "R_FM_",)
616 char *rec_create_filename(char *buffer)
618 char ext[16];
619 const char *pref = "R_";
621 strcpy(buffer,global_settings.rec_directory);
622 if (!check_dir(buffer))
623 return NULL;
625 if((global_settings.rec_source > AUDIO_SRC_PLAYBACK) &&
626 (global_settings.rec_source < AUDIO_NUM_SOURCES))
628 pref = prestr[global_settings.rec_source];
631 snprintf(ext, sizeof(ext), ".%s",
632 REC_FILE_ENDING(global_settings.rec_format));
634 #if CONFIG_RTC == 0
635 return create_numbered_filename(buffer, buffer, pref, ext, 4,
636 &file_number);
637 #else
638 /* We'll wait at least up to the start of the next second so no duplicate
639 names are created */
640 return create_datetime_filename(buffer, buffer, pref, ext, true);
641 #endif
644 #if CONFIG_RTC == 0
645 /* Hit disk to get a starting filename for the type */
646 static void rec_init_filename(void)
648 file_number = -1;
649 rec_create_filename(path_buffer);
650 file_number--;
652 #endif
654 int rec_create_directory(void)
656 return check_dir(global_settings.rec_directory)?1:0;
659 void rec_init_recording_options(struct audio_recording_options *options)
661 options->rec_source = global_settings.rec_source;
662 options->rec_frequency = global_settings.rec_frequency;
663 options->rec_channels = global_settings.rec_channels;
664 options->rec_prerecord_time = global_settings.rec_prerecord_time;
665 #if CONFIG_CODEC == SWCODEC
666 options->rec_source_flags = 0;
667 options->enc_config.rec_format = global_settings.rec_format;
668 global_to_encoder_config(&options->enc_config);
669 #else
670 options->rec_quality = global_settings.rec_quality;
671 options->rec_editable = global_settings.rec_editable;
672 #endif
675 #if CONFIG_CODEC == SWCODEC && !defined (SIMULATOR)
676 void rec_set_source(int source, unsigned flags)
678 /* Set audio input source, power up/down devices */
679 audio_set_input_source(source, flags);
681 /* Set peakmeters for recording or reset to playback */
682 peak_meter_playback((flags & SRCF_RECORDING) == 0);
683 peak_meter_enabled = true;
685 #endif /* CONFIG_CODEC == SWCODEC && !defined (SIMULATOR) */
687 void rec_set_recording_options(struct audio_recording_options *options)
689 #if CONFIG_CODEC != SWCODEC
690 if (global_settings.rec_prerecord_time)
692 talk_buffer_steal(); /* will use the mp3 buffer */
694 #else /* == SWCODEC */
695 rec_set_source(options->rec_source,
696 options->rec_source_flags | SRCF_RECORDING);
697 #endif /* CONFIG_CODEC != SWCODEC */
699 audio_set_recording_options(options);
702 void rec_command(enum recording_command cmd)
704 switch(cmd)
706 case RECORDING_CMD_STOP:
707 pm_activate_clipcount(false);
708 audio_stop_recording();
709 break;
710 case RECORDING_CMD_START:
711 /* steal mp3 buffer, create unique filename and start recording */
712 pm_reset_clipcount();
713 pm_activate_clipcount(true);
714 #if CONFIG_CODEC != SWCODEC
715 talk_buffer_steal(); /* we use the mp3 buffer */
716 #endif
717 audio_record(rec_create_filename(path_buffer));
718 break;
719 case RECORDING_CMD_START_NEWFILE:
720 /* create unique filename and start recording*/
721 pm_reset_clipcount();
722 pm_activate_clipcount(true); /* just to be sure */
723 audio_new_file(rec_create_filename(path_buffer));
724 break;
725 case RECORDING_CMD_PAUSE:
726 pm_activate_clipcount(false);
727 audio_pause_recording();
728 break;
729 case RECORDING_CMD_RESUME:
730 pm_activate_clipcount(true);
731 audio_resume_recording();
732 break;
736 /* used in trigger_listerner and recording_screen */
737 static unsigned int last_seconds = 0;
740 * Callback function so that the peak meter code can send an event
741 * to this application. This function can be passed to
742 * peak_meter_set_trigger_listener in order to activate the trigger.
744 static void trigger_listener(int trigger_status)
746 switch (trigger_status)
748 case TRIG_GO:
749 if(!(audio_status() & AUDIO_STATUS_RECORD))
751 rec_status |= RCSTAT_HAVE_RECORDED;
752 rec_command(RECORDING_CMD_START);
753 #if CONFIG_CODEC != SWCODEC
754 /* give control to mpeg thread so that it can start
755 recording */
756 yield(); yield(); yield();
757 #endif
760 /* if we're already recording this is a retrigger */
761 else
763 if((audio_status() & AUDIO_STATUS_PAUSE) &&
764 (global_settings.rec_trigger_type == 1))
766 rec_command(RECORDING_CMD_RESUME);
768 /* New file on trig start*/
769 else if (global_settings.rec_trigger_type != 2)
771 rec_command(RECORDING_CMD_START_NEWFILE);
772 /* tell recording_screen to reset the time */
773 last_seconds = 0;
776 break;
778 /* A _change_ to TRIG_READY means the current recording has stopped */
779 case TRIG_READY:
780 if(audio_status() & AUDIO_STATUS_RECORD)
782 switch(global_settings.rec_trigger_type)
784 case 0: /* Stop */
785 rec_command(RECORDING_CMD_STOP);
786 break;
788 case 1: /* Pause */
789 rec_command(RECORDING_CMD_PAUSE);
790 break;
792 case 2: /* New file on trig stop*/
793 rec_command(RECORDING_CMD_START_NEWFILE);
794 /* tell recording_screen to reset the time */
795 last_seconds = 0;
796 break;
799 if (global_settings.rec_trigger_mode != TRIG_MODE_REARM)
801 peak_meter_set_trigger_listener(NULL);
802 peak_meter_trigger(false);
805 break;
809 bool recording_start_automatic = false;
811 bool recording_screen(bool no_source)
813 long button;
814 bool done = false;
815 char buf[32];
816 char buf2[32];
817 int w, h;
818 int update_countdown = 1;
819 unsigned int seconds;
820 int hours, minutes;
821 char filename[13];
822 int last_audio_stat = -1;
823 int audio_stat;
824 #if CONFIG_CODEC == SWCODEC
825 int warning_counter = 0;
826 #define WARNING_PERIOD 7
827 #endif
828 #ifdef HAVE_FMRADIO_REC
829 /* Radio is left on if:
830 * 1) Is was on at the start and the initial source is FM Radio
831 * 2) 1) and the source was never changed to something else
833 int radio_status = (global_settings.rec_source != AUDIO_SRC_FMRADIO) ?
834 FMRADIO_OFF : get_radio_status();
835 #endif
836 #if (CONFIG_LED == LED_REAL)
837 bool led_state = false;
838 int led_countdown = 2;
839 #endif
840 #ifdef HAVE_AGC
841 bool peak_read = false;
842 bool peak_valid = false;
843 int peak_l, peak_r;
844 int balance = 0;
845 bool display_agc[NB_SCREENS];
846 #endif
847 int line[NB_SCREENS];
848 int i;
849 int filename_offset[NB_SCREENS];
850 int pm_y[NB_SCREENS];
851 int trig_xpos[NB_SCREENS];
852 int trig_ypos[NB_SCREENS];
853 int trig_width[NB_SCREENS];
854 /* pm_x = offset pm to put clipcount in front.
855 Use lcd_getstringsize() when not using SYSFONT */
856 int pm_x = global_settings.peak_meter_clipcounter ? 30 : 0;
858 static const unsigned char *byte_units[] = {
859 ID2P(LANG_BYTE),
860 ID2P(LANG_KILOBYTE),
861 ID2P(LANG_MEGABYTE),
862 ID2P(LANG_GIGABYTE)
865 int style = STYLE_INVERT;
866 #ifdef HAVE_LCD_COLOR
867 if (global_settings.cursor_style == 2) {
868 style |= STYLE_COLORBAR;
870 else if (global_settings.cursor_style == 3) {
871 style |= STYLE_GRADIENT | 1;
873 #endif
875 struct audio_recording_options rec_options;
876 if (check_dir(global_settings.rec_directory) == false)
878 do {
879 gui_syncsplash(0, "%s %s",
880 str(LANG_REC_DIR_NOT_WRITABLE),
881 str(LANG_OFF_ABORT));
882 } while (action_userabort(HZ) == false);
883 return false;
886 rec_status = RCSTAT_IN_RECSCREEN;
887 cursor = 0;
888 #if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR)
889 ata_set_led_enabled(false);
890 #endif
892 #if CONFIG_CODEC == SWCODEC
893 /* recording_menu gets messed up: so prevent manus talking */
894 talk_disable(true);
895 /* audio_init_recording stops anything playing when it takes the audio
896 buffer */
897 #else
898 /* Yes, we use the D/A for monitoring */
899 peak_meter_enabled = true;
900 peak_meter_playback(true);
901 #endif
903 audio_init_recording(0);
904 sound_set_volume(global_settings.volume);
906 #ifdef HAVE_AGC
907 peak_meter_get_peakhold(&peak_l, &peak_r);
908 #endif
910 rec_init_recording_options(&rec_options);
911 rec_set_recording_options(&rec_options);
913 set_gain();
915 if(rec_create_directory() > 0)
916 rec_status |= RCSTAT_CREATED_DIRECTORY;
918 #if CONFIG_RTC == 0
919 /* Create new filename for recording start */
920 rec_init_filename();
921 #endif
923 pm_reset_clipcount();
924 pm_activate_clipcount(false);
925 settings_apply_trigger();
927 #ifdef HAVE_AGC
928 agc_preset_str[0] = str(LANG_SYSFONT_OFF);
929 agc_preset_str[1] = str(LANG_SYSFONT_AGC_SAFETY);
930 agc_preset_str[2] = str(LANG_SYSFONT_AGC_LIVE);
931 agc_preset_str[3] = str(LANG_SYSFONT_AGC_DJSET);
932 agc_preset_str[4] = str(LANG_SYSFONT_AGC_MEDIUM);
933 agc_preset_str[5] = str(LANG_SYSFONT_AGC_VOICE);
934 if (global_settings.rec_source == AUDIO_SRC_MIC) {
935 agc_preset = global_settings.rec_agc_preset_mic;
936 agc_maxgain = global_settings.rec_agc_maxgain_mic;
938 else {
939 agc_preset = global_settings.rec_agc_preset_line;
940 agc_maxgain = global_settings.rec_agc_maxgain_line;
942 #endif /* HAVE_AGC */
944 FOR_NB_SCREENS(i)
946 screens[i].setfont(FONT_SYSFIXED);
947 screens[i].getstringsize("M", &w, &h);
948 screens[i].setmargins(global_settings.cursor_style ? 0 : w, 8);
949 filename_offset[i] = ((screens[i].height >= 80) ? 1 : 0);
950 pm_y[i] = 8 + h * (2 + filename_offset[i]);
953 #ifdef HAVE_REMOTE_LCD
954 if (!remote_display_on)
956 screens[1].clear_display();
957 snprintf(buf, sizeof(buf), str(LANG_REMOTE_LCD_ON));
958 screens[1].puts((screens[1].width/w - strlen(buf))/2 + 1,
959 screens[1].height/(h*2) + 1, buf);
960 screens[1].update();
961 gui_syncsplash(0, str(LANG_REMOTE_LCD_OFF));
963 #endif
965 while(!done)
967 audio_stat = audio_status();
969 #if (CONFIG_LED == LED_REAL)
972 * Flash the LED while waiting to record. Turn it on while
973 * recording.
975 if(audio_stat & AUDIO_STATUS_RECORD)
977 if (audio_stat & AUDIO_STATUS_PAUSE)
979 if (--led_countdown <= 0)
981 led_state = !led_state;
982 led(led_state);
983 led_countdown = 2;
986 else
988 /* trigger is on in status TRIG_READY (no check needed) */
989 led(true);
992 else
994 int trigStat = peak_meter_trigger_status();
996 * other trigger stati than trig_off and trig_steady
997 * already imply that we are recording.
999 if (trigStat == TRIG_STEADY)
1001 if (--led_countdown <= 0)
1003 led_state = !led_state;
1004 led(led_state);
1005 led_countdown = 2;
1008 else
1010 /* trigger is on in status TRIG_READY (no check needed) */
1011 led(false);
1014 #endif /* CONFIG_LED */
1016 /* Wait for a button a while (HZ/10) drawing the peak meter */
1017 button = peak_meter_draw_get_btn(pm_x, pm_y, h * PM_HEIGHT, screen_update);
1019 if (last_audio_stat != audio_stat)
1021 if (audio_stat & AUDIO_STATUS_RECORD)
1023 rec_status |= RCSTAT_HAVE_RECORDED;
1025 last_audio_stat = audio_stat;
1029 if (recording_start_automatic)
1031 /* simulate a button press */
1032 button = ACTION_REC_PAUSE;
1033 recording_start_automatic = false;
1036 switch(button)
1038 #ifdef HAVE_REMOTE_LCD
1039 case ACTION_REC_LCD:
1040 if (remote_display_on)
1042 remote_display_on = false;
1043 screen_update = 1;
1044 screens[1].clear_display();
1045 snprintf(buf, sizeof(buf), str(LANG_REMOTE_LCD_ON));
1046 screens[1].puts((screens[1].width/w - strlen(buf))/2 + 1,
1047 screens[1].height/(h*2) + 1, buf);
1048 screens[1].update();
1049 gui_syncsplash(0, str(LANG_REMOTE_LCD_OFF));
1051 else
1053 remote_display_on = true;
1054 screen_update = NB_SCREENS;
1056 break;
1057 #endif
1058 case ACTION_STD_CANCEL:
1059 /* turn off the trigger */
1060 peak_meter_trigger(false);
1061 peak_meter_set_trigger_listener(NULL);
1063 if(audio_stat & AUDIO_STATUS_RECORD)
1065 rec_command(RECORDING_CMD_STOP);
1067 else
1069 #if CONFIG_CODEC != SWCODEC
1070 peak_meter_playback(true);
1071 peak_meter_enabled = false;
1072 #endif
1073 done = true;
1075 update_countdown = 1; /* Update immediately */
1076 break;
1078 case ACTION_REC_PAUSE:
1079 case ACTION_REC_NEWFILE:
1080 /* Only act if the mpeg is stopped */
1081 if(!(audio_stat & AUDIO_STATUS_RECORD))
1083 /* is this manual or triggered recording? */
1084 if ((global_settings.rec_trigger_mode == TRIG_MODE_OFF) ||
1085 (peak_meter_trigger_status() != TRIG_OFF))
1087 /* manual recording */
1088 rec_status |= RCSTAT_HAVE_RECORDED;
1089 rec_command(RECORDING_CMD_START);
1090 last_seconds = 0;
1091 if (global_settings.talk_menu)
1093 /* no voice possible here, but a beep */
1094 audio_beep(HZ/2); /* longer beep on start */
1097 /* this is triggered recording */
1098 else
1100 /* we don't start recording now, but enable the
1101 trigger and let the callback function
1102 trigger_listener control when the recording starts */
1103 peak_meter_trigger(true);
1104 peak_meter_set_trigger_listener(&trigger_listener);
1107 else
1109 /*if new file button pressed, start new file */
1110 if (button == ACTION_REC_NEWFILE)
1112 rec_command(RECORDING_CMD_START_NEWFILE);
1113 last_seconds = 0;
1115 else
1116 /* if pause button pressed, pause or resume */
1118 if(audio_stat & AUDIO_STATUS_PAUSE)
1120 rec_command(RECORDING_CMD_RESUME);
1121 if (global_settings.talk_menu)
1123 /* no voice possible here, but a beep */
1124 audio_beep(HZ/4); /* short beep on resume */
1127 else
1129 rec_command(RECORDING_CMD_PAUSE);
1133 update_countdown = 1; /* Update immediately */
1134 break;
1136 case ACTION_STD_PREV:
1137 cursor--;
1138 adjust_cursor();
1139 update_countdown = 1; /* Update immediately */
1140 break;
1142 case ACTION_STD_NEXT:
1143 cursor++;
1144 adjust_cursor();
1145 update_countdown = 1; /* Update immediately */
1146 break;
1148 case ACTION_SETTINGS_INC:
1149 switch(cursor)
1151 case 0:
1152 global_settings.volume++;
1153 setvol();
1154 break;
1155 case 1:
1156 if(global_settings.rec_source == AUDIO_SRC_MIC)
1158 if(global_settings.rec_mic_gain <
1159 sound_max(SOUND_MIC_GAIN))
1160 global_settings.rec_mic_gain++;
1162 else
1164 if(global_settings.rec_left_gain <
1165 sound_max(SOUND_LEFT_GAIN))
1166 global_settings.rec_left_gain++;
1167 if(global_settings.rec_right_gain <
1168 sound_max(SOUND_RIGHT_GAIN))
1169 global_settings.rec_right_gain++;
1171 break;
1172 case 2:
1173 if(global_settings.rec_left_gain <
1174 sound_max(SOUND_LEFT_GAIN))
1175 global_settings.rec_left_gain++;
1176 break;
1177 case 3:
1178 if(global_settings.rec_right_gain <
1179 sound_max(SOUND_RIGHT_GAIN))
1180 global_settings.rec_right_gain++;
1181 break;
1182 #ifdef HAVE_AGC
1183 case 4:
1184 agc_preset = MIN(agc_preset + 1, AGC_MODE_SIZE);
1185 agc_enable = (agc_preset != 0);
1186 if (global_settings.rec_source == AUDIO_SRC_MIC) {
1187 global_settings.rec_agc_preset_mic = agc_preset;
1188 agc_maxgain = global_settings.rec_agc_maxgain_mic;
1189 } else {
1190 global_settings.rec_agc_preset_line = agc_preset;
1191 agc_maxgain = global_settings.rec_agc_maxgain_line;
1193 break;
1194 case 5:
1195 if (global_settings.rec_source == AUDIO_SRC_MIC)
1197 agc_maxgain = MIN(agc_maxgain + 1,
1198 sound_max(SOUND_MIC_GAIN));
1199 global_settings.rec_agc_maxgain_mic = agc_maxgain;
1201 else
1203 agc_maxgain = MIN(agc_maxgain + 1,
1204 sound_max(SOUND_LEFT_GAIN));
1205 global_settings.rec_agc_maxgain_line = agc_maxgain;
1207 break;
1208 #endif /* HAVE_AGC */
1210 set_gain();
1211 update_countdown = 1; /* Update immediately */
1212 break;
1214 case ACTION_SETTINGS_DEC:
1215 switch(cursor)
1217 case 0:
1218 global_settings.volume--;
1219 setvol();
1220 break;
1221 case 1:
1222 if(global_settings.rec_source == AUDIO_SRC_MIC)
1224 if(global_settings.rec_mic_gain >
1225 sound_min(SOUND_MIC_GAIN))
1226 global_settings.rec_mic_gain--;
1228 else
1230 if(global_settings.rec_left_gain >
1231 sound_min(SOUND_LEFT_GAIN))
1232 global_settings.rec_left_gain--;
1233 if(global_settings.rec_right_gain >
1234 sound_min(SOUND_RIGHT_GAIN))
1235 global_settings.rec_right_gain--;
1237 break;
1238 case 2:
1239 if(global_settings.rec_left_gain >
1240 sound_min(SOUND_LEFT_GAIN))
1241 global_settings.rec_left_gain--;
1242 break;
1243 case 3:
1244 if(global_settings.rec_right_gain >
1245 sound_min(SOUND_RIGHT_GAIN))
1246 global_settings.rec_right_gain--;
1247 break;
1248 #ifdef HAVE_AGC
1249 case 4:
1250 agc_preset = MAX(agc_preset - 1, 0);
1251 agc_enable = (agc_preset != 0);
1252 if (global_settings.rec_source == AUDIO_SRC_MIC) {
1253 global_settings.rec_agc_preset_mic = agc_preset;
1254 agc_maxgain = global_settings.rec_agc_maxgain_mic;
1255 } else {
1256 global_settings.rec_agc_preset_line = agc_preset;
1257 agc_maxgain = global_settings.rec_agc_maxgain_line;
1259 break;
1260 case 5:
1261 if (global_settings.rec_source == AUDIO_SRC_MIC)
1263 agc_maxgain = MAX(agc_maxgain - 1,
1264 sound_min(SOUND_MIC_GAIN));
1265 global_settings.rec_agc_maxgain_mic = agc_maxgain;
1267 else
1269 agc_maxgain = MAX(agc_maxgain - 1,
1270 sound_min(SOUND_LEFT_GAIN));
1271 global_settings.rec_agc_maxgain_line = agc_maxgain;
1273 break;
1274 #endif /* HAVE_AGC */
1276 set_gain();
1277 update_countdown = 1; /* Update immediately */
1278 break;
1280 case ACTION_STD_MENU:
1281 #if CONFIG_CODEC == SWCODEC
1282 if(!(audio_stat & AUDIO_STATUS_RECORD))
1283 #else
1284 if(audio_stat != AUDIO_STATUS_RECORD)
1285 #endif
1287 #ifdef HAVE_FMRADIO_REC
1288 const int prev_rec_source = global_settings.rec_source;
1289 #endif
1291 #if (CONFIG_LED == LED_REAL)
1292 /* led is restored at begin of loop / end of function */
1293 led(false);
1294 #endif
1295 if (recording_menu(no_source))
1297 done = true;
1298 rec_status |= RCSTAT_BEEN_IN_USB_MODE;
1299 #ifdef HAVE_FMRADIO_REC
1300 radio_status = FMRADIO_OFF;
1301 #endif
1303 else
1305 #ifdef HAVE_FMRADIO_REC
1306 /* If input changes away from FM Radio, radio will
1307 remain off when recording screen closes. */
1308 if (global_settings.rec_source != prev_rec_source
1309 && prev_rec_source == AUDIO_SRC_FMRADIO)
1310 radio_status = FMRADIO_OFF;
1311 #endif
1313 #if CONFIG_CODEC == SWCODEC
1314 /* reinit after submenu exit */
1315 audio_close_recording();
1316 audio_init_recording(0);
1317 #endif
1319 rec_init_recording_options(&rec_options);
1320 rec_set_recording_options(&rec_options);
1322 if(rec_create_directory() > 0)
1323 rec_status |= RCSTAT_CREATED_DIRECTORY;
1325 #if CONFIG_CODEC == SWCODEC && CONFIG_RTC == 0
1326 /* If format changed, a new number is required */
1327 rec_init_filename();
1328 #endif
1330 #ifdef HAVE_AGC
1331 if (global_settings.rec_source == AUDIO_SRC_MIC) {
1332 agc_preset = global_settings.rec_agc_preset_mic;
1333 agc_maxgain = global_settings.rec_agc_maxgain_mic;
1335 else {
1336 agc_preset = global_settings.rec_agc_preset_line;
1337 agc_maxgain = global_settings.rec_agc_maxgain_line;
1339 #endif
1341 adjust_cursor();
1342 set_gain();
1343 update_countdown = 1; /* Update immediately */
1345 FOR_NB_SCREENS(i)
1347 screens[i].setfont(FONT_SYSFIXED);
1348 screens[i].setmargins(
1349 global_settings.cursor_style ? 0 : w, 8);
1353 break;
1355 #if CONFIG_KEYPAD == RECORDER_PAD
1356 case ACTION_REC_F2:
1357 if(audio_stat != AUDIO_STATUS_RECORD)
1359 #if (CONFIG_LED == LED_REAL)
1360 /* led is restored at begin of loop / end of function */
1361 led(false);
1362 #endif
1363 if (f2_rec_screen())
1365 rec_status |= RCSTAT_HAVE_RECORDED;
1366 done = true;
1368 else
1369 update_countdown = 1; /* Update immediately */
1371 break;
1373 case ACTION_REC_F3:
1374 if(audio_stat & AUDIO_STATUS_RECORD)
1376 rec_command(RECORDING_CMD_START_NEWFILE);
1377 last_seconds = 0;
1379 else
1381 #if (CONFIG_LED == LED_REAL)
1382 /* led is restored at begin of loop / end of function */
1383 led(false);
1384 #endif
1385 if (f3_rec_screen())
1387 rec_status |= RCSTAT_HAVE_RECORDED;
1388 done = true;
1390 else
1391 update_countdown = 1; /* Update immediately */
1393 break;
1394 #endif /* CONFIG_KEYPAD == RECORDER_PAD */
1396 case SYS_USB_CONNECTED:
1397 /* Only accept USB connection when not recording */
1398 if(!(audio_stat & AUDIO_STATUS_RECORD))
1400 default_event_handler(SYS_USB_CONNECTED);
1401 done = true;
1402 rec_status |= RCSTAT_BEEN_IN_USB_MODE;
1403 #ifdef HAVE_FMRADIO_REC
1404 radio_status = FMRADIO_OFF;
1405 #endif
1407 break;
1409 default:
1410 default_event_handler(button);
1411 break;
1412 } /* end switch */
1414 #ifdef HAVE_AGC
1415 peak_read = !peak_read;
1416 if (peak_read) { /* every 2nd run of loop */
1417 peak_time++;
1418 peak_valid = read_peak_levels(&peak_l, &peak_r, &balance);
1421 /* Handle AGC every 200ms when enabled and peak data is valid */
1422 if (peak_read && agc_enable && peak_valid)
1423 auto_gain_control(&peak_l, &peak_r, &balance);
1424 #endif
1426 FOR_NB_SCREENS(i)
1427 screens[i].setfont(FONT_SYSFIXED);
1429 seconds = audio_recorded_time() / HZ;
1431 update_countdown--;
1432 if(update_countdown == 0 || seconds > last_seconds)
1434 unsigned int dseconds, dhours, dminutes;
1435 unsigned long num_recorded_bytes, dsize, dmb;
1436 int pos = 0;
1438 update_countdown = 5;
1439 last_seconds = seconds;
1441 dseconds = rec_timesplit_seconds();
1442 dsize = rec_sizesplit_bytes();
1443 num_recorded_bytes = audio_num_recorded_bytes();
1445 for(i = 0; i < screen_update; i++)
1446 screens[i].clear_display();
1448 #if CONFIG_CODEC == SWCODEC
1449 if ((audio_stat & AUDIO_STATUS_WARNING)
1450 && (warning_counter++ % WARNING_PERIOD) < WARNING_PERIOD/2)
1452 /* Switch back and forth displaying warning on first available
1453 line to ensure visibility - the motion should also help
1454 draw attention */
1455 /* Don't use language string unless agreed upon to make this
1456 method permanent - could do something in the statusbar */
1457 snprintf(buf, sizeof(buf), "Warning: %08X",
1458 pcm_rec_get_warnings());
1460 else
1461 #endif /* CONFIG_CODEC == SWCODEC */
1462 if ((global_settings.rec_sizesplit) && (global_settings.rec_split_method))
1464 dmb = dsize/1024/1024;
1465 snprintf(buf, sizeof(buf), "%s %dMB",
1466 str(LANG_SYSFONT_SPLIT_SIZE), dmb);
1468 else
1470 hours = seconds / 3600;
1471 minutes = (seconds - (hours * 3600)) / 60;
1472 snprintf(buf, sizeof(buf), "%s %02d:%02d:%02d",
1473 str(LANG_SYSFONT_RECORDING_TIME),
1474 hours, minutes, seconds%60);
1477 for(i = 0; i < screen_update; i++)
1478 screens[i].puts(0, 0, buf);
1480 if(audio_stat & AUDIO_STATUS_PRERECORD)
1482 snprintf(buf, sizeof(buf), "%s...", str(LANG_SYSFONT_RECORD_PRERECORD));
1484 else
1486 /* Display the split interval if the record timesplit
1487 is active */
1488 if ((global_settings.rec_timesplit) && !(global_settings.rec_split_method))
1490 /* Display the record timesplit interval rather
1491 than the file size if the record timer is
1492 active */
1493 dhours = dseconds / 3600;
1494 dminutes = (dseconds - (dhours * 3600)) / 60;
1495 snprintf(buf, sizeof(buf), "%s %02d:%02d",
1496 str(LANG_SYSFONT_RECORD_TIMESPLIT_REC),
1497 dhours, dminutes);
1499 else
1501 output_dyn_value(buf2, sizeof buf2,
1502 num_recorded_bytes,
1503 byte_units, true);
1504 snprintf(buf, sizeof(buf), "%s %s",
1505 str(LANG_SYSFONT_RECORDING_SIZE), buf2);
1508 for(i = 0; i < screen_update; i++)
1509 screens[i].puts(0, 1, buf);
1511 for(i = 0; i < screen_update; i++)
1513 if (filename_offset[i] > 0)
1515 *filename = '\0';
1516 if (audio_stat & AUDIO_STATUS_RECORD)
1518 strncpy(filename, path_buffer +
1519 strlen(path_buffer) - 12, 13);
1520 filename[12]='\0';
1523 snprintf(buf, sizeof(buf), "%s %s",
1524 str(LANG_SYSFONT_RECORDING_FILENAME), filename);
1525 screens[i].puts(0, 2, buf);
1529 /* We will do file splitting regardless, either at the end of
1530 a split interval, or when the filesize approaches the 2GB
1531 FAT file size (compatibility) limit. */
1532 if ((audio_stat && !(global_settings.rec_split_method)
1533 && global_settings.rec_timesplit && (seconds >= dseconds))
1534 || (audio_stat && global_settings.rec_split_method
1535 && global_settings.rec_sizesplit && (num_recorded_bytes >= dsize))
1536 || (num_recorded_bytes >= MAX_FILE_SIZE))
1538 if (!(global_settings.rec_split_type)
1539 || (num_recorded_bytes >= MAX_FILE_SIZE))
1541 rec_command(RECORDING_CMD_START_NEWFILE);
1542 last_seconds = 0;
1544 else
1546 peak_meter_trigger(false);
1547 peak_meter_set_trigger_listener(NULL);
1548 rec_command(RECORDING_CMD_STOP);
1550 update_countdown = 1;
1553 /* draw the clipcounter just in front of the peakmeter */
1554 if(global_settings.peak_meter_clipcounter)
1556 char clpstr[32];
1557 snprintf(clpstr, 32, "%4d", pm_get_clipcount());
1558 for(i = 0; i < screen_update; i++)
1560 if(PM_HEIGHT > 1)
1561 screens[i].puts(0, 2 + filename_offset[i],
1562 str(LANG_SYSFONT_PM_CLIPCOUNT));
1563 screens[i].puts(0, 1 + PM_HEIGHT + filename_offset[i],
1564 clpstr);
1568 snprintf(buf, sizeof(buf), "%s: %s", str(LANG_SYSFONT_VOLUME),
1569 fmt_gain(SOUND_VOLUME,
1570 global_settings.volume,
1571 buf2, sizeof(buf2)));
1573 if (global_settings.cursor_style && (pos++ == cursor))
1575 for(i = 0; i < screen_update; i++)
1576 screens[i].puts_style_offset(0, filename_offset[i] +
1577 PM_HEIGHT + 2, buf, style,0);
1579 else
1581 for(i = 0; i < screen_update; i++)
1582 screens[i].puts(0, filename_offset[i] + PM_HEIGHT + 2, buf);
1585 if(global_settings.rec_source == AUDIO_SRC_MIC)
1587 /* Draw MIC recording gain */
1588 snprintf(buf, sizeof(buf), "%s:%s", str(LANG_SYSFONT_GAIN),
1589 fmt_gain(SOUND_MIC_GAIN,
1590 global_settings.rec_mic_gain,
1591 buf2, sizeof(buf2)));
1592 if(global_settings.cursor_style && ((1==cursor)||(2==cursor)))
1594 for(i = 0; i < screen_update; i++)
1595 screens[i].puts_style_offset(0, filename_offset[i] +
1596 PM_HEIGHT + 3, buf, style,0);
1598 else
1600 for(i = 0; i < screen_update; i++)
1601 screens[i].puts(0, filename_offset[i] +
1602 PM_HEIGHT + 3, buf);
1605 else if(0
1606 HAVE_LINE_REC_( || global_settings.rec_source == AUDIO_SRC_LINEIN)
1607 HAVE_FMRADIO_REC_( || global_settings.rec_source == AUDIO_SRC_FMRADIO)
1610 /* Draw LINE or FMRADIO recording gain */
1611 snprintf(buf, sizeof(buf), "%s:%s",
1612 str(LANG_SYSFONT_RECORDING_LEFT),
1613 fmt_gain(SOUND_LEFT_GAIN,
1614 global_settings.rec_left_gain,
1615 buf2, sizeof(buf2)));
1616 if(global_settings.cursor_style && ((1==cursor)||(2==cursor)))
1618 for(i = 0; i < screen_update; i++)
1619 screens[i].puts_style_offset(0, filename_offset[i] +
1620 PM_HEIGHT + 3, buf, style,0);
1622 else
1624 for(i = 0; i < screen_update; i++)
1625 screens[i].puts(0, filename_offset[i] +
1626 PM_HEIGHT + 3, buf);
1629 snprintf(buf, sizeof(buf), "%s:%s",
1630 str(LANG_SYSFONT_RECORDING_RIGHT),
1631 fmt_gain(SOUND_RIGHT_GAIN,
1632 global_settings.rec_right_gain,
1633 buf2, sizeof(buf2)));
1634 if(global_settings.cursor_style && ((1==cursor)||(3==cursor)))
1636 for(i = 0; i < screen_update; i++)
1637 screens[i].puts_style_offset(0, filename_offset[i] +
1638 PM_HEIGHT + 4, buf, style,0);
1640 else
1642 for(i = 0; i < screen_update; i++)
1643 screens[i].puts(0, filename_offset[i] +
1644 PM_HEIGHT + 4, buf);
1648 FOR_NB_SCREENS(i)
1650 switch (global_settings.rec_source)
1652 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
1653 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
1654 line[i] = 5;
1655 break;
1657 case AUDIO_SRC_MIC:
1658 line[i] = 4;
1659 break;
1660 #ifdef HAVE_SPDIF_REC
1661 case AUDIO_SRC_SPDIF:
1662 line[i] = 3;
1663 break;
1664 #endif
1665 default:
1666 line[i] = 5; /* to prevent uninitialisation
1667 warnings for line[0] */
1668 break;
1669 } /* end switch */
1670 #ifdef HAVE_AGC
1671 if (screens[i].height < h * (2 + filename_offset[i] + PM_HEIGHT + line[i]))
1673 line[i] -= 1;
1674 display_agc[i] = false;
1676 else
1677 display_agc[i] = true;
1679 if ((cursor==4) || (cursor==5))
1680 display_agc[i] = true;
1683 /************** AGC test info ******************
1684 snprintf(buf, sizeof(buf), "D:%d U:%d",
1685 (agc_droptime+2)/5, (agc_risetime+2)/5);
1686 lcd_putsxy(1, LCD_HEIGHT - 8, buf);
1687 snprintf(buf, sizeof(buf), "B:%d",
1688 (agc_baltime+2)/5);
1689 lcd_putsxy(LCD_WIDTH/2 + 3, LCD_HEIGHT - 8, buf);
1690 ***********************************************/
1692 if (cursor == 5)
1693 snprintf(buf, sizeof(buf), "%s: %s",
1694 str(LANG_SYSFONT_RECORDING_AGC_MAXGAIN),
1695 fmt_gain(SOUND_LEFT_GAIN,
1696 agc_maxgain, buf2, sizeof(buf2)));
1697 else if (agc_preset == 0)
1698 snprintf(buf, sizeof(buf), "%s: %s",
1699 str(LANG_SYSFONT_RECORDING_AGC_PRESET),
1700 agc_preset_str[agc_preset]);
1701 else if (global_settings.rec_source == AUDIO_SRC_MIC)
1702 snprintf(buf, sizeof(buf), "%s: %s%s",
1703 str(LANG_SYSFONT_RECORDING_AGC_PRESET),
1704 agc_preset_str[agc_preset],
1705 fmt_gain(SOUND_LEFT_GAIN,
1706 agc_maxgain -
1707 global_settings.rec_mic_gain,
1708 buf2, sizeof(buf2)));
1709 else
1710 snprintf(buf, sizeof(buf), "%s: %s%s",
1711 str(LANG_SYSFONT_RECORDING_AGC_PRESET),
1712 agc_preset_str[agc_preset],
1713 fmt_gain(SOUND_LEFT_GAIN,
1714 agc_maxgain -
1715 (global_settings.rec_left_gain +
1716 global_settings.rec_right_gain)/2,
1717 buf2, sizeof(buf2)));
1719 if(global_settings.cursor_style && ((cursor==4) || (cursor==5)))
1721 for(i = 0; i < screen_update; i++)
1722 screens[i].puts_style_offset(0, filename_offset[i] +
1723 PM_HEIGHT + line[i], buf, style,0);
1725 else if (
1726 global_settings.rec_source == AUDIO_SRC_MIC
1727 HAVE_LINE_REC_(|| global_settings.rec_source == AUDIO_SRC_LINEIN)
1728 HAVE_FMRADIO_REC_(|| global_settings.rec_source == AUDIO_SRC_FMRADIO)
1731 for(i = 0; i < screen_update; i++) {
1732 if (display_agc[i]) {
1733 screens[i].puts(0, filename_offset[i] +
1734 PM_HEIGHT + line[i], buf);
1739 if (global_settings.rec_source == AUDIO_SRC_MIC)
1741 if(agc_maxgain < (global_settings.rec_mic_gain))
1742 change_recording_gain(false, true, true);
1744 else
1746 if(agc_maxgain < (global_settings.rec_left_gain))
1747 change_recording_gain(false, true, false);
1748 if(agc_maxgain < (global_settings.rec_right_gain))
1749 change_recording_gain(false, false, true);
1751 #else /* !HAVE_AGC */
1753 #endif /* HAVE_AGC */
1755 if(!global_settings.cursor_style) {
1756 switch(cursor)
1758 case 1:
1759 for(i = 0; i < screen_update; i++)
1760 screen_put_cursorxy(&screens[i], 0,
1761 filename_offset[i] +
1762 PM_HEIGHT + 3, true);
1764 if(global_settings.rec_source != AUDIO_SRC_MIC)
1766 for(i = 0; i < screen_update; i++)
1767 screen_put_cursorxy(&screens[i], 0,
1768 filename_offset[i] +
1769 PM_HEIGHT + 4, true);
1771 break;
1772 case 2:
1773 for(i = 0; i < screen_update; i++)
1774 screen_put_cursorxy(&screens[i], 0,
1775 filename_offset[i] +
1776 PM_HEIGHT + 3, true);
1777 break;
1778 case 3:
1779 for(i = 0; i < screen_update; i++)
1780 screen_put_cursorxy(&screens[i], 0,
1781 filename_offset[i] +
1782 PM_HEIGHT + 4, true);
1783 break;
1784 #ifdef HAVE_AGC
1785 case 4:
1786 case 5:
1787 for(i = 0; i < screen_update; i++)
1788 screen_put_cursorxy(&screens[i], 0,
1789 filename_offset[i] +
1790 PM_HEIGHT + line[i], true);
1791 break;
1792 #endif /* HAVE_AGC */
1793 default:
1794 for(i = 0; i < screen_update; i++)
1795 screen_put_cursorxy(&screens[i], 0,
1796 filename_offset[i] +
1797 PM_HEIGHT + 2, true);
1801 #ifdef HAVE_AGC
1802 hist_time++;
1803 #endif
1805 for(i = 0; i < screen_update; i++)
1807 gui_statusbar_draw(&(statusbars.statusbars[i]), true);
1808 peak_meter_screen(&screens[i], pm_x, pm_y[i], h*PM_HEIGHT);
1809 screens[i].update();
1812 /* draw the trigger status */
1813 FOR_NB_SCREENS(i)
1815 trig_width[i] = ((screens[i].height < 64) ||
1816 ((screens[i].height < 72) && (PM_HEIGHT > 1))) ?
1817 screens[i].width - 14 * w : screens[i].width;
1818 trig_xpos[i] = screens[i].width - trig_width[i];
1819 trig_ypos[i] = ((screens[i].height < 72) && (PM_HEIGHT > 1)) ?
1820 h*2 :
1821 h*(1 + filename_offset[i] + PM_HEIGHT + line[i]
1822 #ifdef HAVE_AGC
1824 #endif
1828 if (peak_meter_trigger_status() != TRIG_OFF)
1830 peak_meter_draw_trig(trig_xpos, trig_ypos, trig_width,
1831 screen_update);
1832 for(i = 0; i < screen_update; i++){
1833 screens[i].update_rect(trig_xpos[i], trig_ypos[i],
1834 trig_width[i] + 2, TRIG_HEIGHT);
1839 if(audio_stat & AUDIO_STATUS_ERROR)
1841 done = true;
1843 } /* end while(!done) */
1845 audio_stat = audio_status();
1846 if (audio_stat & AUDIO_STATUS_ERROR)
1848 gui_syncsplash(0, str(LANG_SYSFONT_DISK_FULL));
1849 gui_syncstatusbar_draw(&statusbars, true);
1851 FOR_NB_SCREENS(i)
1852 screens[i].update();
1854 audio_error_clear();
1856 while(1)
1858 if (action_userabort(TIMEOUT_NOBLOCK))
1859 break;
1863 #if CONFIG_CODEC == SWCODEC
1864 rec_command(RECORDING_CMD_STOP);
1865 audio_close_recording();
1867 #ifdef HAVE_FMRADIO_REC
1868 if (radio_status != FMRADIO_OFF)
1869 /* Restore radio playback - radio_status should be unchanged if started
1870 through fm radio screen (barring usb connect) */
1871 rec_set_source(AUDIO_SRC_FMRADIO, (radio_status & FMRADIO_PAUSED) ?
1872 SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
1873 else
1874 #endif
1875 /* Go back to playback mode */
1876 rec_set_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
1878 /* restore talking */
1879 talk_disable(false);
1880 #else /* !SWCODEC */
1881 audio_init_playback();
1882 #endif /* CONFIG_CODEC == SWCODEC */
1884 /* make sure the trigger is really turned off */
1885 peak_meter_trigger(false);
1886 peak_meter_set_trigger_listener(NULL);
1888 rec_status &= ~RCSTAT_IN_RECSCREEN;
1889 sound_settings_apply();
1891 FOR_NB_SCREENS(i)
1892 screens[i].setfont(FONT_UI);
1894 /* if the directory was created or recording happened, make sure the
1895 browser is updated */
1896 if (rec_status & (RCSTAT_CREATED_DIRECTORY | RCSTAT_HAVE_RECORDED))
1897 reload_directory();
1899 #if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR)
1900 ata_set_led_enabled(true);
1901 #endif
1903 settings_save();
1905 return (rec_status & RCSTAT_BEEN_IN_USB_MODE) != 0;
1906 } /* recording_screen */
1908 #if CONFIG_KEYPAD == RECORDER_PAD
1909 static bool f2_rec_screen(void)
1911 static const char* const freq_str[6] =
1913 "44.1kHz",
1914 "48kHz",
1915 "32kHz",
1916 "22.05kHz",
1917 "24kHz",
1918 "16kHz"
1921 bool exit = false;
1922 bool used = false;
1923 int w, h, i;
1924 char buf[32];
1925 int button;
1926 struct audio_recording_options rec_options;
1928 FOR_NB_SCREENS(i)
1930 screens[i].setfont(FONT_SYSFIXED);
1931 screens[i].getstringsize("A",&w,&h);
1934 while (!exit) {
1935 const char* ptr=NULL;
1937 FOR_NB_SCREENS(i)
1939 screens[i].clear_display();
1941 /* Recording quality */
1942 screens[i].putsxy(0, LCD_HEIGHT/2 - h*2,
1943 str(LANG_SYSFONT_RECORDING_QUALITY));
1946 snprintf(buf, sizeof(buf), "%d", global_settings.rec_quality);
1947 FOR_NB_SCREENS(i)
1949 screens[i].putsxy(0, LCD_HEIGHT/2-h, buf);
1950 screens[i].mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
1951 LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8);
1954 /* Frequency */
1955 snprintf(buf, sizeof buf, "%s:", str(LANG_SYSFONT_RECORDING_FREQUENCY));
1956 ptr = freq_str[global_settings.rec_frequency];
1957 FOR_NB_SCREENS(i)
1959 screens[i].getstringsize(buf,&w,&h);
1960 screens[i].putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h*2, buf);
1961 screens[i].getstringsize(ptr, &w, &h);
1962 screens[i].putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h, ptr);
1963 screens[i].mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
1964 LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8);
1967 /* Channel mode */
1968 switch ( global_settings.rec_channels ) {
1969 case 0:
1970 ptr = str(LANG_SYSFONT_CHANNEL_STEREO);
1971 break;
1973 case 1:
1974 ptr = str(LANG_SYSFONT_CHANNEL_MONO);
1975 break;
1978 FOR_NB_SCREENS(i)
1980 screens[i].getstringsize(str(LANG_SYSFONT_CHANNELS), &w, &h);
1981 screens[i].putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h*2,
1982 str(LANG_SYSFONT_CHANNELS));
1983 screens[i].getstringsize(str(LANG_SYSFONT_MODE), &w, &h);
1984 screens[i].putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h,
1985 str(LANG_SYSFONT_MODE));
1986 screens[i].getstringsize(ptr, &w, &h);
1987 screens[i].putsxy(LCD_WIDTH - w, LCD_HEIGHT/2, ptr);
1988 screens[i].mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
1989 LCD_WIDTH/2 + 8, LCD_HEIGHT/2 - 4, 7, 8);
1991 screens[i].update();
1994 button = button_get(true);
1995 switch (button) {
1996 case BUTTON_LEFT:
1997 case BUTTON_F2 | BUTTON_LEFT:
1998 global_settings.rec_quality++;
1999 if(global_settings.rec_quality > 7)
2000 global_settings.rec_quality = 0;
2001 used = true;
2002 break;
2004 case BUTTON_DOWN:
2005 case BUTTON_F2 | BUTTON_DOWN:
2006 global_settings.rec_frequency++;
2007 if(global_settings.rec_frequency > 5)
2008 global_settings.rec_frequency = 0;
2009 used = true;
2010 break;
2012 case BUTTON_RIGHT:
2013 case BUTTON_F2 | BUTTON_RIGHT:
2014 global_settings.rec_channels++;
2015 if(global_settings.rec_channels > 1)
2016 global_settings.rec_channels = 0;
2017 used = true;
2018 break;
2020 case BUTTON_F2 | BUTTON_REL:
2021 if ( used )
2022 exit = true;
2023 used = true;
2024 break;
2026 case BUTTON_F2 | BUTTON_REPEAT:
2027 used = true;
2028 break;
2030 default:
2031 if(default_event_handler(button) == SYS_USB_CONNECTED)
2032 return true;
2033 break;
2037 rec_init_recording_options(&rec_options);
2038 rec_set_recording_options(&rec_options);
2040 set_gain();
2042 settings_save();
2043 FOR_NB_SCREENS(i)
2044 screens[i].setfont(FONT_UI);
2046 return false;
2049 static bool f3_rec_screen(void)
2051 bool exit = false;
2052 bool used = false;
2053 int w, h, i;
2054 int button;
2055 char *src_str[] =
2057 str(LANG_SYSFONT_RECORDING_SRC_MIC),
2058 str(LANG_SYSFONT_LINE_IN),
2059 str(LANG_SYSFONT_RECORDING_SRC_DIGITAL)
2061 struct audio_recording_options rec_options;
2063 FOR_NB_SCREENS(i)
2065 screens[i].setfont(FONT_SYSFIXED);
2066 screens[i].getstringsize("A",&w,&h);
2069 while (!exit) {
2070 char* ptr=NULL;
2071 ptr = src_str[global_settings.rec_source];
2072 FOR_NB_SCREENS(i)
2074 screens[i].clear_display();
2076 /* Recording source */
2077 screens[i].putsxy(0, LCD_HEIGHT/2 - h*2,
2078 str(LANG_SYSFONT_RECORDING_SOURCE));
2080 screens[i].getstringsize(ptr, &w, &h);
2081 screens[i].putsxy(0, LCD_HEIGHT/2-h, ptr);
2082 screens[i].mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
2083 LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8);
2086 /* trigger setup */
2087 ptr = str(LANG_SYSFONT_RECORD_TRIGGER);
2088 FOR_NB_SCREENS(i)
2090 screens[i].getstringsize(ptr,&w,&h);
2091 screens[i].putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h*2, ptr);
2092 screens[i].mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
2093 LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8);
2095 screens[i].update();
2098 button = button_get(true);
2099 switch (button) {
2100 case BUTTON_DOWN:
2101 case BUTTON_F3 | BUTTON_DOWN:
2102 #ifndef SIMULATOR
2103 rectrigger();
2104 settings_apply_trigger();
2105 #endif
2106 exit = true;
2107 break;
2109 case BUTTON_LEFT:
2110 case BUTTON_F3 | BUTTON_LEFT:
2111 global_settings.rec_source++;
2112 if(global_settings.rec_source > AUDIO_SRC_MAX)
2113 global_settings.rec_source = 0;
2114 used = true;
2115 break;
2117 case BUTTON_F3 | BUTTON_REL:
2118 if ( used )
2119 exit = true;
2120 used = true;
2121 break;
2123 case BUTTON_F3 | BUTTON_REPEAT:
2124 used = true;
2125 break;
2127 default:
2128 if(default_event_handler(button) == SYS_USB_CONNECTED)
2129 return true;
2130 break;
2134 rec_init_recording_options(&rec_options);
2135 rec_set_recording_options(&rec_options);
2137 set_gain();
2139 settings_save();
2140 FOR_NB_SCREENS(i)
2141 screens[i].setfont(FONT_UI);
2143 return false;
2145 #endif /* CONFIG_KEYPAD == RECORDER_PAD */
2147 #if CONFIG_CODEC == SWCODEC
2148 void audio_beep(int duration)
2150 /* dummy */
2151 (void)duration;
2154 #ifdef SIMULATOR
2155 /* stubs for recording sim */
2156 void audio_init_recording(unsigned int buffer_offset)
2158 buffer_offset = buffer_offset;
2161 void audio_close_recording(void)
2165 unsigned long pcm_rec_get_warnings(void)
2167 return 0;
2170 unsigned long audio_recorded_time(void)
2172 return 123;
2175 unsigned long audio_num_recorded_bytes(void)
2177 return 5 * 1024 * 1024;
2180 void rec_set_source(int source, unsigned flags)
2182 source = source;
2183 flags = flags;
2186 void audio_set_recording_options(struct audio_recording_options *options)
2188 options = options;
2191 void audio_set_recording_gain(int left, int right, int type)
2193 left = left;
2194 right = right;
2195 type = type;
2198 void audio_record(const char *filename)
2200 filename = filename;
2203 void audio_new_file(const char *filename)
2205 filename = filename;
2208 void audio_stop_recording(void)
2212 void audio_pause_recording(void)
2216 void audio_resume_recording(void)
2220 #endif /* #ifdef SIMULATOR */
2221 #endif /* #ifdef CONFIG_CODEC == SWCODEC */
2223 #endif /* HAVE_RECORDING */