rockbox/apps/recorder/recording.c
Nils Wallménius b311367481 *** Lang v2 cleanup (FS#6574) ***
1) Introduces apps/features.txt that controls which strings are included 
for each target based on defines.
2) .lng and .voice files are now target specific and the format versions 
of both these file types have been bumped, which means that new voice 
files are needed. 
3) Use the 'features' mechanism to exclude strings for targets that 
didn't use them.
4) Delete unused and deprecated and duplicated strings, sort strings in 
english.lang

Some string IDs were changed so translations will be slightly worse than 
before.



git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14198 a1c6a512-1295-4272-9138-f99709370657
2007-08-05 19:19:39 +00:00

2167 lines
69 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Linus Nielsen Feltzing
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include "system.h"
#include "power.h"
#include "lcd.h"
#include "led.h"
#include "mpeg.h"
#include "audio.h"
#if CONFIG_CODEC == SWCODEC
#include "thread.h"
#include "pcm_playback.h"
#include "playback.h"
#include "enc_config.h"
#if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT)
#include "spdif.h"
#endif
#endif /* CONFIG_CODEC == SWCODEC */
#include "recording.h"
#include "mp3_playback.h"
#include "mas.h"
#include "button.h"
#include "kernel.h"
#include "settings.h"
#include "lang.h"
#include "font.h"
#include "icons.h"
#include "icon.h"
#include "screens.h"
#include "peakmeter.h"
#include "statusbar.h"
#include "menu.h"
#include "sound_menu.h"
#include "timefuncs.h"
#include "debug.h"
#include "misc.h"
#include "tree.h"
#include "string.h"
#include "dir.h"
#include "errno.h"
#include "talk.h"
#include "atoi.h"
#include "sound.h"
#include "ata.h"
#include "splash.h"
#include "screen_access.h"
#include "action.h"
#include "radio.h"
#ifdef HAVE_RECORDING
/* This array holds the record timer interval lengths, in seconds */
static const unsigned long rec_timer_seconds[] =
{
0, /* 0 means OFF */
5*60, /* 00:05 */
10*60, /* 00:10 */
15*60, /* 00:15 */
30*60, /* 00:30 */
60*60, /* 01:00 */
74*60, /* 74:00 */
80*60, /* 80:00 */
2*60*60, /* 02:00 */
4*60*60, /* 04:00 */
6*60*60, /* 06:00 */
8*60*60, /* 08:00 */
10L*60*60, /* 10:00 */
12L*60*60, /* 12:00 */
18L*60*60, /* 18:00 */
24L*60*60 /* 24:00 */
};
static unsigned int rec_timesplit_seconds(void)
{
return rec_timer_seconds[global_settings.rec_timesplit];
}
/* This array holds the record size interval lengths, in bytes */
static const unsigned long rec_size_bytes[] =
{
0, /* 0 means OFF */
5*1024*1024, /* 5MB */
10*1024*1024, /* 10MB */
15*1024*1024, /* 15MB */
32*1024*1024, /* 32MB */
64*1024*1024, /* 64MB */
75*1024*1024, /* 75MB */
100*1024*1024, /* 100MB */
128*1024*1024, /* 128MB */
256*1024*1024, /* 256MB */
512*1024*1024, /* 512MB */
650*1024*1024, /* 650MB */
700*1024*1024, /* 700MB */
1024*1024*1024, /* 1GB */
1536*1024*1024, /* 1.5GB */
1792*1024*1024, /* 1.75GB */
};
static unsigned long rec_sizesplit_bytes(void)
{
return rec_size_bytes[global_settings.rec_sizesplit];
}
/*
* Time strings used for the trigger durations.
* Keep synchronous to trigger_times in settings_apply_trigger
*/
const char * const trig_durations[TRIG_DURATION_COUNT] =
{
"0s", "1s", "2s", "5s",
"10s", "15s", "20s", "25s", "30s",
"1min", "2min", "5min", "10min"
};
void settings_apply_trigger(void)
{
/* Keep synchronous to trig_durations and trig_durations_conf*/
static const long trigger_times[TRIG_DURATION_COUNT] = {
0, HZ, 2*HZ, 5*HZ,
10*HZ, 15*HZ, 20*HZ, 25*HZ, 30*HZ,
60*HZ, 2*60*HZ, 5*60*HZ, 10*60*HZ
};
peak_meter_define_trigger(
global_settings.rec_start_thres,
trigger_times[global_settings.rec_start_duration],
MIN(trigger_times[global_settings.rec_start_duration] / 2, 2*HZ),
global_settings.rec_stop_thres,
trigger_times[global_settings.rec_stop_postrec],
trigger_times[global_settings.rec_stop_gap]
);
}
/* recording screen status flags */
enum rec_status_flags
{
RCSTAT_IN_RECSCREEN = 0x00000001,
RCSTAT_BEEN_IN_USB_MODE = 0x00000002,
RCSTAT_CREATED_DIRECTORY = 0x00000004,
RCSTAT_HAVE_RECORDED = 0x00000008,
};
static int rec_status = 0;
bool in_recording_screen(void)
{
return (rec_status & RCSTAT_IN_RECSCREEN) != 0;
}
#define PM_HEIGHT ((LCD_HEIGHT >= 72) ? 2 : 1)
#if CONFIG_KEYPAD == RECORDER_PAD
static bool f2_rec_screen(void);
static bool f3_rec_screen(void);
#endif
#define MAX_FILE_SIZE 0x7F800000 /* 2 GB - 4 MB */
static int screen_update = NB_SCREENS;
#ifdef HAVE_REMOTE_LCD
static bool remote_display_on = true;
#endif
/** File name creation **/
#if CONFIG_RTC == 0
/* current file number to assist in creating unique numbered filenames
without actually having to create the file on disk */
static int file_number = -1;
#endif /* CONFIG_RTC */
#if CONFIG_CODEC == SWCODEC
#define REC_FILE_ENDING(rec_format) \
(audio_formats[rec_format_afmt[rec_format]].ext_list)
#else /* CONFIG_CODEC != SWCODEC */
/* default record file extension for HWCODEC */
#define REC_FILE_ENDING(rec_format) \
(audio_formats[AFMT_MPA_L3].ext_list)
#endif /* CONFIG_CODEC == SWCODEC */
/* path for current file */
static char path_buffer[MAX_PATH];
/** Automatic Gain Control (AGC) **/
#ifdef HAVE_AGC
/* Timing counters:
* peak_time is incremented every 0.2s, every 2nd run of record screen loop.
* hist_time is incremented every 0.5s, display update.
* peak_time is the counter of the peak hold read and agc process,
* overflow every 13 years 8-)
*/
static long peak_time = 0;
static long hist_time = 0;
static short peak_valid_mem[4];
#define BAL_MEM_SIZE 24
static short balance_mem[BAL_MEM_SIZE];
/* Automatic Gain Control */
#define AGC_MODE_SIZE 5
#define AGC_SAFETY_MODE 0
static const char* agc_preset_str[] =
{ "Off", "S", "L", "D", "M", "V" };
/* "Off",
"Safety (clip)",
"Live (slow)",
"DJ-Set (slow)",
"Medium",
"Voice (fast)" */
#define AGC_CLIP 32766
#define AGC_PEAK 29883 /* fast gain reduction threshold -0.8dB */
#define AGC_HIGH 27254 /* accelerated gain reduction threshold -1.6dB */
#define AGC_IMG 823 /* threshold for balance control -32dB */
/* autogain high level thresholds (-3dB, -7dB, -4dB, -5dB, -5dB) */
static const short agc_th_hi[AGC_MODE_SIZE] =
{ 23197, 14637, 21156, 18428, 18426 };
/* autogain low level thresholds (-14dB, -11dB, -6dB, -7dB, -8dB) */
static const short agc_th_lo[AGC_MODE_SIZE] =
{ 6538, 9235, 16422, 14636, 13045 };
/* autogain threshold times [1/5s] or [200ms] */
static const short agc_tdrop[AGC_MODE_SIZE] =
{ 900, 225, 150, 60, 8 };
static const short agc_trise[AGC_MODE_SIZE] =
{ 9000, 750, 400, 150, 20 };
static const short agc_tbal[AGC_MODE_SIZE] =
{ 4500, 500, 300, 100, 15 };
/* AGC operation */
static bool agc_enable = true;
static short agc_preset;
/* AGC levels */
static int agc_left = 0;
static int agc_right = 0;
/* AGC time since high target volume was exceeded */
static short agc_droptime = 0;
/* AGC time since volume fallen below low target */
static short agc_risetime = 0;
/* AGC balance time exceeding +/- 0.7dB */
static short agc_baltime = 0;
/* AGC maximum gain */
static short agc_maxgain;
#endif /* HAVE_AGC */
static void set_gain(void)
{
if(global_settings.rec_source == AUDIO_SRC_MIC)
{
audio_set_recording_gain(global_settings.rec_mic_gain,
0, AUDIO_GAIN_MIC);
}
else
{
/* AUDIO_SRC_LINEIN, AUDIO_SRC_FMRADIO, AUDIO_SRC_SPDIF */
audio_set_recording_gain(global_settings.rec_left_gain,
global_settings.rec_right_gain,
AUDIO_GAIN_LINEIN);
}
}
#ifdef HAVE_AGC
/* Read peak meter values & calculate balance.
* Returns validity of peak values.
* Used for automatic gain control and history diagram.
*/
static bool read_peak_levels(int *peak_l, int *peak_r, int *balance)
{
peak_meter_get_peakhold(peak_l, peak_r);
peak_valid_mem[peak_time % 3] = *peak_l;
if (((peak_valid_mem[0] == peak_valid_mem[1]) &&
(peak_valid_mem[1] == peak_valid_mem[2])) &&
((*peak_l < 32767)
#ifndef SIMULATOR
|| ata_disk_is_active()
#endif
))
return false;
if (*peak_r > *peak_l)
balance_mem[peak_time % BAL_MEM_SIZE] = (*peak_l ?
MIN((10000 * *peak_r) / *peak_l - 10000, 15118) : 15118);
else
balance_mem[peak_time % BAL_MEM_SIZE] = (*peak_r ?
MAX(10000 - (10000 * *peak_l) / *peak_r, -15118) : -15118);
*balance = 0;
int i;
for (i = 0; i < BAL_MEM_SIZE; i++)
*balance += balance_mem[i];
*balance = *balance / BAL_MEM_SIZE;
return true;
}
/* AGC helper function to check if maximum gain is reached */
static bool agc_gain_is_max(bool left, bool right)
{
/* range -128...+108 [0.5dB] */
short gain_current_l;
short gain_current_r;
if (agc_preset == 0)
return false;
switch (global_settings.rec_source)
{
HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
gain_current_l = global_settings.rec_left_gain;
gain_current_r = global_settings.rec_right_gain;
break;
case AUDIO_SRC_MIC:
default:
gain_current_l = global_settings.rec_mic_gain;
gain_current_r = global_settings.rec_mic_gain;
}
return ((left && (gain_current_l >= agc_maxgain)) ||
(right && (gain_current_r >= agc_maxgain)));
}
static void change_recording_gain(bool increment, bool left, bool right)
{
int factor = (increment ? 1 : -1);
switch (global_settings.rec_source)
{
HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
if (left) global_settings.rec_left_gain += factor;
if (right) global_settings.rec_right_gain += factor;
break;
case AUDIO_SRC_MIC:
global_settings.rec_mic_gain += factor;
}
}
/*
* Handle automatic gain control (AGC).
* Change recording gain if peak_x levels are above or below
* target volume for specified timeouts.
*/
static void auto_gain_control(int *peak_l, int *peak_r, int *balance)
{
int agc_mono;
short agc_mode;
bool increment;
if (*peak_l > agc_left)
agc_left = *peak_l;
else
agc_left -= (agc_left - *peak_l + 3) >> 2;
if (*peak_r > agc_right)
agc_right = *peak_r;
else
agc_right -= (agc_right - *peak_r + 3) >> 2;
agc_mono = (agc_left + agc_right) / 2;
agc_mode = abs(agc_preset) - 1;
if (agc_mode < 0) {
agc_enable = false;
return;
}
if (agc_mode != AGC_SAFETY_MODE) {
/* Automatic balance control - only if not in safety mode */
if ((agc_left > AGC_IMG) && (agc_right > AGC_IMG))
{
if (*balance < -556)
{
if (*balance > -900)
agc_baltime -= !(peak_time % 4); /* 0.47 - 0.75dB */
else if (*balance > -4125)
agc_baltime--; /* 0.75 - 3.00dB */
else if (*balance > -7579)
agc_baltime -= 2; /* 3.00 - 4.90dB */
else
agc_baltime -= !(peak_time % 8); /* 4.90 - inf dB */
if (agc_baltime > 0)
agc_baltime -= (peak_time % 2);
}
else if (*balance > 556)
{
if (*balance < 900)
agc_baltime += !(peak_time % 4);
else if (*balance < 4125)
agc_baltime++;
else if (*balance < 7579)
agc_baltime += 2;
else
agc_baltime += !(peak_time % 8);
if (agc_baltime < 0)
agc_baltime += (peak_time % 2);
}
if ((*balance * agc_baltime) < 0)
{
if (*balance < 0)
agc_baltime -= peak_time % 2;
else
agc_baltime += peak_time % 2;
}
increment = ((agc_risetime / 2) > agc_droptime);
if (agc_baltime < -agc_tbal[agc_mode])
{
if (!increment || !agc_gain_is_max(!increment, increment)) {
change_recording_gain(increment, !increment, increment);
set_gain();
}
agc_baltime = 0;
}
else if (agc_baltime > +agc_tbal[agc_mode])
{
if (!increment || !agc_gain_is_max(increment, !increment)) {
change_recording_gain(increment, increment, !increment);
set_gain();
}
agc_baltime = 0;
}
}
else if (!(hist_time % 4))
{
if (agc_baltime < 0)
agc_baltime++;
else
agc_baltime--;
}
}
/* Automatic gain control */
if ((agc_left > agc_th_hi[agc_mode]) || (agc_right > agc_th_hi[agc_mode]))
{
if ((agc_left > AGC_CLIP) || (agc_right > AGC_CLIP))
agc_droptime += agc_tdrop[agc_mode] /
(global_settings.rec_agc_cliptime + 1);
if (agc_left > AGC_HIGH) {
agc_droptime++;
agc_risetime=0;
if (agc_left > AGC_PEAK)
agc_droptime += 2;
}
if (agc_right > AGC_HIGH) {
agc_droptime++;
agc_risetime=0;
if (agc_right > AGC_PEAK)
agc_droptime += 2;
}
if (agc_mono > agc_th_hi[agc_mode])
agc_droptime++;
else
agc_droptime += !(peak_time % 2);
if (agc_droptime >= agc_tdrop[agc_mode])
{
change_recording_gain(false, true, true);
agc_droptime = 0;
agc_risetime = 0;
set_gain();
}
agc_risetime = MAX(agc_risetime - 1, 0);
}
else if (agc_mono < agc_th_lo[agc_mode])
{
if (agc_mono < (agc_th_lo[agc_mode] / 8))
agc_risetime += !(peak_time % 5);
else if (agc_mono < (agc_th_lo[agc_mode] / 2))
agc_risetime += 2;
else
agc_risetime++;
if (agc_risetime >= agc_trise[agc_mode]) {
if ((agc_mode != AGC_SAFETY_MODE) &&
(!agc_gain_is_max(true, true))) {
change_recording_gain(true, true, true);
set_gain();
}
agc_risetime = 0;
agc_droptime = 0;
}
agc_droptime = MAX(agc_droptime - 1, 0);
}
else if (!(peak_time % 6)) /* on target level every 1.2 sec */
{
agc_risetime = MAX(agc_risetime - 1, 0);
agc_droptime = MAX(agc_droptime - 1, 0);
}
}
#endif /* HAVE_AGC */
static const char* const fmtstr[] =
{
"%c%d %s", /* no decimals */
"%c%d.%d %s ", /* 1 decimal */
"%c%d.%02d %s " /* 2 decimals */
};
static char *fmt_gain(int snd, int val, char *str, int len)
{
int i, d, numdec;
const char *unit;
char sign = ' ';
val = sound_val2phys(snd, val);
if(val < 0)
{
sign = '-';
val = -val;
}
numdec = sound_numdecimals(snd);
unit = sound_unit(snd);
if(numdec)
{
i = val / (10*numdec);
d = val % (10*numdec);
snprintf(str, len, fmtstr[numdec], sign, i, d, unit);
}
else
snprintf(str, len, fmtstr[numdec], sign, val, unit);
return str;
}
static int cursor;
static void adjust_cursor(void)
{
int max_cursor;
if(cursor < 0)
cursor = 0;
#ifdef HAVE_AGC
switch(global_settings.rec_source)
{
case REC_SRC_MIC:
if(cursor == 2)
cursor = 4;
else if(cursor == 3)
cursor = 1;
HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
max_cursor = 5;
break;
default:
max_cursor = 0;
break;
}
#else /* !HAVE_AGC */
switch(global_settings.rec_source)
{
case AUDIO_SRC_MIC:
max_cursor = 1;
break;
HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
max_cursor = 3;
break;
default:
max_cursor = 0;
break;
}
#endif /* HAVE_AGC */
if(cursor > max_cursor)
cursor = max_cursor;
}
static bool check_dir(char *folder)
{
DIR *dir = opendir(folder);
if (!dir && strcmp(folder, "/"))
{
int rc = mkdir(folder);
if(rc < 0)
return false;
return true;
}
closedir(dir);
return true;
}
/* the list below must match enum audio_sources in audio.h */
static const char* const prestr[] =
{
HAVE_MIC_IN_([AUDIO_SRC_MIC] = "R_MIC_",)
HAVE_LINE_REC_([AUDIO_SRC_LINEIN] = "R_LINE_",)
HAVE_SPDIF_IN_([AUDIO_SRC_SPDIF] = "R_SPDIF_",)
HAVE_FMRADIO_REC_([AUDIO_SRC_FMRADIO] = "R_FM_",)
};
char *rec_create_filename(char *buffer)
{
char ext[16];
const char *pref = "R_";
strcpy(buffer,global_settings.rec_directory);
if (!check_dir(buffer))
return NULL;
if((global_settings.rec_source > AUDIO_SRC_PLAYBACK) &&
(global_settings.rec_source < AUDIO_NUM_SOURCES))
{
pref = prestr[global_settings.rec_source];
}
snprintf(ext, sizeof(ext), ".%s",
REC_FILE_ENDING(global_settings.rec_format));
#if CONFIG_RTC == 0
return create_numbered_filename(buffer, buffer, pref, ext, 4,
&file_number);
#else
/* We'll wait at least up to the start of the next second so no duplicate
names are created */
return create_datetime_filename(buffer, buffer, pref, ext, true);
#endif
}
#if CONFIG_RTC == 0
/* Hit disk to get a starting filename for the type */
void rec_init_filename(void)
{
file_number = -1;
rec_create_filename(path_buffer);
file_number--;
}
#endif
int rec_create_directory(void)
{
return check_dir(global_settings.rec_directory)?1:0;
}
void rec_init_recording_options(struct audio_recording_options *options)
{
options->rec_source = global_settings.rec_source;
options->rec_frequency = global_settings.rec_frequency;
options->rec_channels = global_settings.rec_channels;
options->rec_prerecord_time = global_settings.rec_prerecord_time;
#if CONFIG_CODEC == SWCODEC
options->rec_source_flags = 0;
options->enc_config.rec_format = global_settings.rec_format;
global_to_encoder_config(&options->enc_config);
#else
options->rec_quality = global_settings.rec_quality;
options->rec_editable = global_settings.rec_editable;
#endif
}
#if CONFIG_CODEC == SWCODEC && !defined (SIMULATOR)
void rec_set_source(int source, unsigned flags)
{
/* Set audio input source, power up/down devices */
audio_set_input_source(source, flags);
/* Set peakmeters for recording or reset to playback */
peak_meter_playback((flags & SRCF_RECORDING) == 0);
peak_meter_enabled = true;
}
#endif /* CONFIG_CODEC == SWCODEC && !defined (SIMULATOR) */
void rec_set_recording_options(struct audio_recording_options *options)
{
#if CONFIG_CODEC != SWCODEC
if (global_settings.rec_prerecord_time)
talk_buffer_steal(); /* will use the mp3 buffer */
#else /* == SWOCODEC */
rec_set_source(options->rec_source,
options->rec_source_flags | SRCF_RECORDING);
#endif /* CONFIG_CODEC != SWCODEC */
audio_set_recording_options(options);
}
/* steals mp3 buffer, creates unique filename and starts recording */
void rec_record(void)
{
#if CONFIG_CODEC != SWCODEC
talk_buffer_steal(); /* we use the mp3 buffer */
#endif
audio_record(rec_create_filename(path_buffer));
}
/* creates unique filename and starts recording */
void rec_new_file(void)
{
audio_new_file(rec_create_filename(path_buffer));
}
/* used in trigger_listerner and recording_screen */
static unsigned int last_seconds = 0;
/**
* Callback function so that the peak meter code can send an event
* to this application. This function can be passed to
* peak_meter_set_trigger_listener in order to activate the trigger.
*/
static void trigger_listener(int trigger_status)
{
switch (trigger_status)
{
case TRIG_GO:
if(!(audio_status() & AUDIO_STATUS_RECORD))
{
rec_status |= RCSTAT_HAVE_RECORDED;
rec_record();
#if CONFIG_CODEC != SWCODEC
/* give control to mpeg thread so that it can start
recording */
yield(); yield(); yield();
#endif
}
/* if we're already recording this is a retrigger */
else
{
if((audio_status() & AUDIO_STATUS_PAUSE) &&
(global_settings.rec_trigger_type == 1))
audio_resume_recording();
/* New file on trig start*/
else if (global_settings.rec_trigger_type != 2)
{
rec_new_file();
/* tell recording_screen to reset the time */
last_seconds = 0;
}
}
break;
/* A _change_ to TRIG_READY means the current recording has stopped */
case TRIG_READY:
if(audio_status() & AUDIO_STATUS_RECORD)
{
switch(global_settings.rec_trigger_type)
{
case 0: /* Stop */
audio_stop_recording();
break;
case 1: /* Pause */
audio_pause_recording();
break;
case 2: /* New file on trig stop*/
rec_new_file();
/* tell recording_screen to reset the time */
last_seconds = 0;
break;
}
if (global_settings.rec_trigger_mode != TRIG_MODE_REARM)
{
peak_meter_set_trigger_listener(NULL);
peak_meter_trigger(false);
}
}
break;
}
}
bool recording_start_automatic = false;
bool recording_screen(bool no_source)
{
long button;
bool done = false;
char buf[32];
char buf2[32];
int w, h;
int update_countdown = 1;
unsigned int seconds;
int hours, minutes;
char filename[13];
int last_audio_stat = -1;
int audio_stat;
#if CONFIG_CODEC == SWCODEC
int warning_counter = 0;
#define WARNING_PERIOD 7
#endif
#ifdef HAVE_FMRADIO_REC
/* Radio is left on if:
* 1) Is was on at the start and the initial source is FM Radio
* 2) 1) and the source was never changed to something else
*/
int radio_status = (global_settings.rec_source != AUDIO_SRC_FMRADIO) ?
FMRADIO_OFF : get_radio_status();
#endif
#if (CONFIG_LED == LED_REAL)
bool led_state = false;
int led_countdown = 2;
#endif
#ifdef HAVE_AGC
bool peak_read = false;
bool peak_valid = false;
int peak_l, peak_r;
int balance = 0;
bool display_agc[NB_SCREENS];
#endif
int line[NB_SCREENS];
int i;
int filename_offset[NB_SCREENS];
int pm_y[NB_SCREENS];
int trig_xpos[NB_SCREENS];
int trig_ypos[NB_SCREENS];
int trig_width[NB_SCREENS];
static const unsigned char *byte_units[] = {
ID2P(LANG_BYTE),
ID2P(LANG_KILOBYTE),
ID2P(LANG_MEGABYTE),
ID2P(LANG_GIGABYTE)
};
struct audio_recording_options rec_options;
if (check_dir(global_settings.rec_directory) == false)
{
do {
gui_syncsplash(0, "%s %s",
str(LANG_REC_DIR_NOT_WRITABLE),
str(LANG_OFF_ABORT));
} while (action_userabort(HZ) == false);
return false;
}
rec_status = RCSTAT_IN_RECSCREEN;
cursor = 0;
#if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR)
ata_set_led_enabled(false);
#endif
#if CONFIG_CODEC == SWCODEC
/* recording_menu gets messed up: so prevent manus talking */
talk_disable_menus();
/* audio_init_recording stops anything playing when it takes the audio
buffer */
#else
/* Yes, we use the D/A for monitoring */
peak_meter_enabled = true;
peak_meter_playback(true);
#endif
audio_init_recording(0);
sound_set_volume(global_settings.volume);
#ifdef HAVE_AGC
peak_meter_get_peakhold(&peak_l, &peak_r);
#endif
rec_init_recording_options(&rec_options);
rec_set_recording_options(&rec_options);
set_gain();
if(rec_create_directory() > 0)
rec_status |= RCSTAT_CREATED_DIRECTORY;
#if CONFIG_RTC == 0
/* Create new filename for recording start */
rec_init_filename();
#endif
settings_apply_trigger();
#ifdef HAVE_AGC
agc_preset_str[0] = str(LANG_SYSFONT_OFF);
agc_preset_str[1] = str(LANG_SYSFONT_AGC_SAFETY);
agc_preset_str[2] = str(LANG_SYSFONT_AGC_LIVE);
agc_preset_str[3] = str(LANG_SYSFONT_AGC_DJSET);
agc_preset_str[4] = str(LANG_SYSFONT_AGC_MEDIUM);
agc_preset_str[5] = str(LANG_SYSFONT_AGC_VOICE);
if (global_settings.rec_source == AUDIO_SRC_MIC) {
agc_preset = global_settings.rec_agc_preset_mic;
agc_maxgain = global_settings.rec_agc_maxgain_mic;
}
else {
agc_preset = global_settings.rec_agc_preset_line;
agc_maxgain = global_settings.rec_agc_maxgain_line;
}
#endif /* HAVE_AGC */
FOR_NB_SCREENS(i)
{
screens[i].setfont(FONT_SYSFIXED);
screens[i].getstringsize("M", &w, &h);
screens[i].setmargins(global_settings.invert_cursor ? 0 : w, 8);
filename_offset[i] = ((screens[i].height >= 80) ? 1 : 0);
pm_y[i] = 8 + h * (2 + filename_offset[i]);
}
#ifdef HAVE_REMOTE_LCD
if (!remote_display_on)
{
screens[1].clear_display();
snprintf(buf, sizeof(buf), str(LANG_REMOTE_LCD_ON));
screens[1].puts((screens[1].width/w - strlen(buf))/2 + 1,
screens[1].height/(h*2) + 1, buf);
screens[1].update();
gui_syncsplash(0, str(LANG_REMOTE_LCD_OFF));
}
#endif
while(!done)
{
audio_stat = audio_status();
#if (CONFIG_LED == LED_REAL)
/*
* Flash the LED while waiting to record. Turn it on while
* recording.
*/
if(audio_stat & AUDIO_STATUS_RECORD)
{
if (audio_stat & AUDIO_STATUS_PAUSE)
{
if (--led_countdown <= 0)
{
led_state = !led_state;
led(led_state);
led_countdown = 2;
}
}
else
{
/* trigger is on in status TRIG_READY (no check needed) */
led(true);
}
}
else
{
int trigStat = peak_meter_trigger_status();
/*
* other trigger stati than trig_off and trig_steady
* already imply that we are recording.
*/
if (trigStat == TRIG_STEADY)
{
if (--led_countdown <= 0)
{
led_state = !led_state;
led(led_state);
led_countdown = 2;
}
}
else
{
/* trigger is on in status TRIG_READY (no check needed) */
led(false);
}
}
#endif /* CONFIG_LED */
/* Wait for a button a while (HZ/10) drawing the peak meter */
button = peak_meter_draw_get_btn(0, pm_y, h * PM_HEIGHT, screen_update);
if (last_audio_stat != audio_stat)
{
if (audio_stat & AUDIO_STATUS_RECORD)
{
rec_status |= RCSTAT_HAVE_RECORDED;
}
last_audio_stat = audio_stat;
}
if (recording_start_automatic)
{
/* simulate a button press */
button = ACTION_REC_PAUSE;
recording_start_automatic = false;
}
switch(button)
{
#ifdef HAVE_REMOTE_LCD
case ACTION_REC_LCD:
if (remote_display_on)
{
remote_display_on = false;
screen_update = 1;
screens[1].clear_display();
snprintf(buf, sizeof(buf), str(LANG_REMOTE_LCD_ON));
screens[1].puts((screens[1].width/w - strlen(buf))/2 + 1,
screens[1].height/(h*2) + 1, buf);
screens[1].update();
gui_syncsplash(0, str(LANG_REMOTE_LCD_OFF));
}
else
{
remote_display_on = true;
screen_update = NB_SCREENS;
}
break;
#endif
case ACTION_STD_CANCEL:
/* turn off the trigger */
peak_meter_trigger(false);
peak_meter_set_trigger_listener(NULL);
if(audio_stat & AUDIO_STATUS_RECORD)
{
audio_stop_recording();
}
else
{
#if CONFIG_CODEC != SWCODEC
peak_meter_playback(true);
peak_meter_enabled = false;
#endif
done = true;
}
update_countdown = 1; /* Update immediately */
break;
case ACTION_REC_PAUSE:
case ACTION_REC_NEWFILE:
/* Only act if the mpeg is stopped */
if(!(audio_stat & AUDIO_STATUS_RECORD))
{
/* is this manual or triggered recording? */
if ((global_settings.rec_trigger_mode == TRIG_MODE_OFF) ||
(peak_meter_trigger_status() != TRIG_OFF))
{
/* manual recording */
rec_status |= RCSTAT_HAVE_RECORDED;
rec_record();
last_seconds = 0;
if (global_settings.talk_menu)
{
/* no voice possible here, but a beep */
audio_beep(HZ/2); /* longer beep on start */
}
}
/* this is triggered recording */
else
{
/* we don't start recording now, but enable the
trigger and let the callback function
trigger_listener control when the recording starts */
peak_meter_trigger(true);
peak_meter_set_trigger_listener(&trigger_listener);
}
}
else
{
/*if new file button pressed, start new file */
if (button == ACTION_REC_NEWFILE)
{
rec_new_file();
last_seconds = 0;
}
else
/* if pause button pressed, pause or resume */
{
if(audio_stat & AUDIO_STATUS_PAUSE)
{
audio_resume_recording();
if (global_settings.talk_menu)
{
/* no voice possible here, but a beep */
audio_beep(HZ/4); /* short beep on resume */
}
}
else
{
audio_pause_recording();
}
}
}
update_countdown = 1; /* Update immediately */
break;
case ACTION_STD_PREV:
cursor--;
adjust_cursor();
update_countdown = 1; /* Update immediately */
break;
case ACTION_STD_NEXT:
cursor++;
adjust_cursor();
update_countdown = 1; /* Update immediately */
break;
case ACTION_SETTINGS_INC:
switch(cursor)
{
case 0:
global_settings.volume++;
setvol();
break;
case 1:
if(global_settings.rec_source == AUDIO_SRC_MIC)
{
if(global_settings.rec_mic_gain <
sound_max(SOUND_MIC_GAIN))
global_settings.rec_mic_gain++;
}
else
{
if(global_settings.rec_left_gain <
sound_max(SOUND_LEFT_GAIN))
global_settings.rec_left_gain++;
if(global_settings.rec_right_gain <
sound_max(SOUND_RIGHT_GAIN))
global_settings.rec_right_gain++;
}
break;
case 2:
if(global_settings.rec_left_gain <
sound_max(SOUND_LEFT_GAIN))
global_settings.rec_left_gain++;
break;
case 3:
if(global_settings.rec_right_gain <
sound_max(SOUND_RIGHT_GAIN))
global_settings.rec_right_gain++;
break;
#ifdef HAVE_AGC
case 4:
agc_preset = MIN(agc_preset + 1, AGC_MODE_SIZE);
agc_enable = (agc_preset != 0);
if (global_settings.rec_source == AUDIO_SRC_MIC) {
global_settings.rec_agc_preset_mic = agc_preset;
agc_maxgain = global_settings.rec_agc_maxgain_mic;
} else {
global_settings.rec_agc_preset_line = agc_preset;
agc_maxgain = global_settings.rec_agc_maxgain_line;
}
break;
case 5:
if (global_settings.rec_source == AUDIO_SRC_MIC)
{
agc_maxgain = MIN(agc_maxgain + 1,
sound_max(SOUND_MIC_GAIN));
global_settings.rec_agc_maxgain_mic = agc_maxgain;
}
else
{
agc_maxgain = MIN(agc_maxgain + 1,
sound_max(SOUND_LEFT_GAIN));
global_settings.rec_agc_maxgain_line = agc_maxgain;
}
break;
#endif /* HAVE_AGC */
}
set_gain();
update_countdown = 1; /* Update immediately */
break;
case ACTION_SETTINGS_DEC:
switch(cursor)
{
case 0:
global_settings.volume--;
setvol();
break;
case 1:
if(global_settings.rec_source == AUDIO_SRC_MIC)
{
if(global_settings.rec_mic_gain >
sound_min(SOUND_MIC_GAIN))
global_settings.rec_mic_gain--;
}
else
{
if(global_settings.rec_left_gain >
sound_min(SOUND_LEFT_GAIN))
global_settings.rec_left_gain--;
if(global_settings.rec_right_gain >
sound_min(SOUND_RIGHT_GAIN))
global_settings.rec_right_gain--;
}
break;
case 2:
if(global_settings.rec_left_gain >
sound_min(SOUND_LEFT_GAIN))
global_settings.rec_left_gain--;
break;
case 3:
if(global_settings.rec_right_gain >
sound_min(SOUND_RIGHT_GAIN))
global_settings.rec_right_gain--;
break;
#ifdef HAVE_AGC
case 4:
agc_preset = MAX(agc_preset - 1, 0);
agc_enable = (agc_preset != 0);
if (global_settings.rec_source == AUDIO_SRC_MIC) {
global_settings.rec_agc_preset_mic = agc_preset;
agc_maxgain = global_settings.rec_agc_maxgain_mic;
} else {
global_settings.rec_agc_preset_line = agc_preset;
agc_maxgain = global_settings.rec_agc_maxgain_line;
}
break;
case 5:
if (global_settings.rec_source == AUDIO_SRC_MIC)
{
agc_maxgain = MAX(agc_maxgain - 1,
sound_min(SOUND_MIC_GAIN));
global_settings.rec_agc_maxgain_mic = agc_maxgain;
}
else
{
agc_maxgain = MAX(agc_maxgain - 1,
sound_min(SOUND_LEFT_GAIN));
global_settings.rec_agc_maxgain_line = agc_maxgain;
}
break;
#endif /* HAVE_AGC */
}
set_gain();
update_countdown = 1; /* Update immediately */
break;
case ACTION_STD_MENU:
#if CONFIG_CODEC == SWCODEC
if(!(audio_stat & AUDIO_STATUS_RECORD))
#else
if(audio_stat != AUDIO_STATUS_RECORD)
#endif
{
#ifdef HAVE_FMRADIO_REC
const int prev_rec_source = global_settings.rec_source;
#endif
#if (CONFIG_LED == LED_REAL)
/* led is restored at begin of loop / end of function */
led(false);
#endif
if (recording_menu(no_source))
{
done = true;
rec_status |= RCSTAT_BEEN_IN_USB_MODE;
#ifdef HAVE_FMRADIO_REC
radio_status = FMRADIO_OFF;
#endif
}
else
{
#ifdef HAVE_FMRADIO_REC
/* If input changes away from FM Radio, radio will
remain off when recording screen closes. */
if (global_settings.rec_source != prev_rec_source
&& prev_rec_source == AUDIO_SRC_FMRADIO)
radio_status = FMRADIO_OFF;
#endif
#if CONFIG_CODEC == SWCODEC
/* reinit after submenu exit */
audio_close_recording();
audio_init_recording(0);
#endif
rec_init_recording_options(&rec_options);
rec_set_recording_options(&rec_options);
if(rec_create_directory() > 0)
rec_status |= RCSTAT_CREATED_DIRECTORY;
#if CONFIG_CODEC == SWCODEC && CONFIG_RTC == 0
/* If format changed, a new number is required */
rec_init_filename();
#endif
#ifdef HAVE_AGC
if (global_settings.rec_source == AUDIO_SRC_MIC) {
agc_preset = global_settings.rec_agc_preset_mic;
agc_maxgain = global_settings.rec_agc_maxgain_mic;
}
else {
agc_preset = global_settings.rec_agc_preset_line;
agc_maxgain = global_settings.rec_agc_maxgain_line;
}
#endif
adjust_cursor();
set_gain();
update_countdown = 1; /* Update immediately */
FOR_NB_SCREENS(i)
{
screens[i].setfont(FONT_SYSFIXED);
screens[i].setmargins(
global_settings.invert_cursor ? 0 : w, 8);
}
}
}
break;
#if CONFIG_KEYPAD == RECORDER_PAD
case ACTION_REC_F2:
if(audio_stat != AUDIO_STATUS_RECORD)
{
#if (CONFIG_LED == LED_REAL)
/* led is restored at begin of loop / end of function */
led(false);
#endif
if (f2_rec_screen())
{
rec_status |= RCSTAT_HAVE_RECORDED;
done = true;
}
else
update_countdown = 1; /* Update immediately */
}
break;
case ACTION_REC_F3:
if(audio_stat & AUDIO_STATUS_RECORD)
{
rec_new_file();
last_seconds = 0;
}
else
{
#if (CONFIG_LED == LED_REAL)
/* led is restored at begin of loop / end of function */
led(false);
#endif
if (f3_rec_screen())
{
rec_status |= RCSTAT_HAVE_RECORDED;
done = true;
}
else
update_countdown = 1; /* Update immediately */
}
break;
#endif /* CONFIG_KEYPAD == RECORDER_PAD */
case SYS_USB_CONNECTED:
/* Only accept USB connection when not recording */
if(!(audio_stat & AUDIO_STATUS_RECORD))
{
default_event_handler(SYS_USB_CONNECTED);
done = true;
rec_status |= RCSTAT_BEEN_IN_USB_MODE;
#ifdef HAVE_FMRADIO_REC
radio_status = FMRADIO_OFF;
#endif
}
break;
default:
default_event_handler(button);
break;
} /* end switch */
#ifdef HAVE_AGC
peak_read = !peak_read;
if (peak_read) { /* every 2nd run of loop */
peak_time++;
peak_valid = read_peak_levels(&peak_l, &peak_r, &balance);
}
/* Handle AGC every 200ms when enabled and peak data is valid */
if (peak_read && agc_enable && peak_valid)
auto_gain_control(&peak_l, &peak_r, &balance);
#endif
FOR_NB_SCREENS(i)
screens[i].setfont(FONT_SYSFIXED);
seconds = audio_recorded_time() / HZ;
update_countdown--;
if(update_countdown == 0 || seconds > last_seconds)
{
unsigned int dseconds, dhours, dminutes;
unsigned long num_recorded_bytes, dsize, dmb;
int pos = 0;
update_countdown = 5;
last_seconds = seconds;
dseconds = rec_timesplit_seconds();
dsize = rec_sizesplit_bytes();
num_recorded_bytes = audio_num_recorded_bytes();
for(i = 0; i < screen_update; i++)
screens[i].clear_display();
#if CONFIG_CODEC == SWCODEC
if ((audio_stat & AUDIO_STATUS_WARNING)
&& (warning_counter++ % WARNING_PERIOD) < WARNING_PERIOD/2)
{
/* Switch back and forth displaying warning on first available
line to ensure visibility - the motion should also help
draw attention */
/* Don't use language string unless agreed upon to make this
method permanent - could do something in the statusbar */
snprintf(buf, sizeof(buf), "Warning: %08X",
pcm_rec_get_warnings());
}
else
#endif /* CONFIG_CODEC == SWCODEC */
if ((global_settings.rec_sizesplit) && (global_settings.rec_split_method))
{
dmb = dsize/1024/1024;
snprintf(buf, sizeof(buf), "%s %dMB",
str(LANG_SYSFONT_SPLIT_SIZE), dmb);
}
else
{
hours = seconds / 3600;
minutes = (seconds - (hours * 3600)) / 60;
snprintf(buf, sizeof(buf), "%s %02d:%02d:%02d",
str(LANG_SYSFONT_RECORDING_TIME),
hours, minutes, seconds%60);
}
for(i = 0; i < screen_update; i++)
screens[i].puts(0, 0, buf);
if(audio_stat & AUDIO_STATUS_PRERECORD)
{
snprintf(buf, sizeof(buf), "%s...", str(LANG_SYSFONT_RECORD_PRERECORD));
}
else
{
/* Display the split interval if the record timesplit
is active */
if ((global_settings.rec_timesplit) && !(global_settings.rec_split_method))
{
/* Display the record timesplit interval rather
than the file size if the record timer is
active */
dhours = dseconds / 3600;
dminutes = (dseconds - (dhours * 3600)) / 60;
snprintf(buf, sizeof(buf), "%s %02d:%02d",
str(LANG_SYSFONT_RECORD_TIMESPLIT_REC),
dhours, dminutes);
}
else
{
output_dyn_value(buf2, sizeof buf2,
num_recorded_bytes,
byte_units, true);
snprintf(buf, sizeof(buf), "%s %s",
str(LANG_SYSFONT_RECORDING_SIZE), buf2);
}
}
for(i = 0; i < screen_update; i++)
screens[i].puts(0, 1, buf);
for(i = 0; i < screen_update; i++)
{
if (filename_offset[i] > 0)
{
*filename = '\0';
if (audio_stat & AUDIO_STATUS_RECORD)
{
strncpy(filename, path_buffer +
strlen(path_buffer) - 12, 13);
filename[12]='\0';
}
snprintf(buf, sizeof(buf), "%s %s",
str(LANG_SYSFONT_RECORDING_FILENAME), filename);
screens[i].puts(0, 2, buf);
}
}
/* We will do file splitting regardless, either at the end of
a split interval, or when the filesize approaches the 2GB
FAT file size (compatibility) limit. */
if ((audio_stat && !(global_settings.rec_split_method)
&& global_settings.rec_timesplit && (seconds >= dseconds))
|| (audio_stat && global_settings.rec_split_method
&& global_settings.rec_sizesplit && (num_recorded_bytes >= dsize))
|| (num_recorded_bytes >= MAX_FILE_SIZE))
{
if (!(global_settings.rec_split_type)
|| (num_recorded_bytes >= MAX_FILE_SIZE))
{
rec_new_file();
last_seconds = 0;
}
else
{
peak_meter_trigger(false);
peak_meter_set_trigger_listener(NULL);
audio_stop_recording();
}
update_countdown = 1;
}
snprintf(buf, sizeof(buf), "%s: %s", str(LANG_SYSFONT_VOLUME),
fmt_gain(SOUND_VOLUME,
global_settings.volume,
buf2, sizeof(buf2)));
if (global_settings.invert_cursor && (pos++ == cursor))
{
for(i = 0; i < screen_update; i++)
screens[i].puts_style_offset(0, filename_offset[i] +
PM_HEIGHT + 2, buf, STYLE_INVERT,0);
}
else
{
for(i = 0; i < screen_update; i++)
screens[i].puts(0, filename_offset[i] + PM_HEIGHT + 2, buf);
}
if(global_settings.rec_source == AUDIO_SRC_MIC)
{
/* Draw MIC recording gain */
snprintf(buf, sizeof(buf), "%s:%s", str(LANG_SYSFONT_GAIN),
fmt_gain(SOUND_MIC_GAIN,
global_settings.rec_mic_gain,
buf2, sizeof(buf2)));
if(global_settings.invert_cursor && ((1==cursor)||(2==cursor)))
{
for(i = 0; i < screen_update; i++)
screens[i].puts_style_offset(0, filename_offset[i] +
PM_HEIGHT + 3, buf, STYLE_INVERT,0);
}
else
{
for(i = 0; i < screen_update; i++)
screens[i].puts(0, filename_offset[i] +
PM_HEIGHT + 3, buf);
}
}
else if(0
HAVE_LINE_REC_( || global_settings.rec_source == AUDIO_SRC_LINEIN)
HAVE_FMRADIO_REC_( || global_settings.rec_source == AUDIO_SRC_FMRADIO)
)
{
/* Draw LINE or FMRADIO recording gain */
snprintf(buf, sizeof(buf), "%s:%s",
str(LANG_SYSFONT_RECORDING_LEFT),
fmt_gain(SOUND_LEFT_GAIN,
global_settings.rec_left_gain,
buf2, sizeof(buf2)));
if(global_settings.invert_cursor && ((1==cursor)||(2==cursor)))
{
for(i = 0; i < screen_update; i++)
screens[i].puts_style_offset(0, filename_offset[i] +
PM_HEIGHT + 3, buf, STYLE_INVERT,0);
}
else
{
for(i = 0; i < screen_update; i++)
screens[i].puts(0, filename_offset[i] +
PM_HEIGHT + 3, buf);
}
snprintf(buf, sizeof(buf), "%s:%s",
str(LANG_SYSFONT_RECORDING_RIGHT),
fmt_gain(SOUND_RIGHT_GAIN,
global_settings.rec_right_gain,
buf2, sizeof(buf2)));
if(global_settings.invert_cursor && ((1==cursor)||(3==cursor)))
{
for(i = 0; i < screen_update; i++)
screens[i].puts_style_offset(0, filename_offset[i] +
PM_HEIGHT + 4, buf, STYLE_INVERT,0);
}
else
{
for(i = 0; i < screen_update; i++)
screens[i].puts(0, filename_offset[i] +
PM_HEIGHT + 4, buf);
}
}
FOR_NB_SCREENS(i)
{
switch (global_settings.rec_source)
{
HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
line[i] = 5;
break;
case AUDIO_SRC_MIC:
line[i] = 4;
break;
#ifdef HAVE_SPDIF_REC
case AUDIO_SRC_SPDIF:
line[i] = 3;
break;
#endif
default:
line[i] = 5; /* to prevent uninitialisation
warnings for line[0] */
break;
} /* end switch */
#ifdef HAVE_AGC
if (screens[i].height < h * (2 + filename_offset[i] + PM_HEIGHT + line[i]))
{
line[i] -= 1;
display_agc[i] = false;
}
else
display_agc[i] = true;
if ((cursor==4) || (cursor==5))
display_agc[i] = true;
}
/************** AGC test info ******************
snprintf(buf, sizeof(buf), "D:%d U:%d",
(agc_droptime+2)/5, (agc_risetime+2)/5);
lcd_putsxy(1, LCD_HEIGHT - 8, buf);
snprintf(buf, sizeof(buf), "B:%d",
(agc_baltime+2)/5);
lcd_putsxy(LCD_WIDTH/2 + 3, LCD_HEIGHT - 8, buf);
***********************************************/
if (cursor == 5)
snprintf(buf, sizeof(buf), "%s: %s",
str(LANG_SYSFONT_RECORDING_AGC_MAXGAIN),
fmt_gain(SOUND_LEFT_GAIN,
agc_maxgain, buf2, sizeof(buf2)));
else if (agc_preset == 0)
snprintf(buf, sizeof(buf), "%s: %s",
str(LANG_SYSFONT_RECORDING_AGC_PRESET),
agc_preset_str[agc_preset]);
else if (global_settings.rec_source == AUDIO_SRC_MIC)
snprintf(buf, sizeof(buf), "%s: %s%s",
str(LANG_SYSFONT_RECORDING_AGC_PRESET),
agc_preset_str[agc_preset],
fmt_gain(SOUND_LEFT_GAIN,
agc_maxgain -
global_settings.rec_mic_gain,
buf2, sizeof(buf2)));
else
snprintf(buf, sizeof(buf), "%s: %s%s",
str(LANG_SYSFONT_RECORDING_AGC_PRESET),
agc_preset_str[agc_preset],
fmt_gain(SOUND_LEFT_GAIN,
agc_maxgain -
(global_settings.rec_left_gain +
global_settings.rec_right_gain)/2,
buf2, sizeof(buf2)));
if(global_settings.invert_cursor && ((cursor==4) || (cursor==5)))
{
for(i = 0; i < screen_update; i++)
screens[i].puts_style_offset(0, filename_offset[i] +
PM_HEIGHT + line[i], buf, STYLE_INVERT,0);
}
else if (
global_settings.rec_source == AUDIO_SRC_MIC
HAVE_LINE_REC_(|| global_settings.rec_source == AUDIO_SRC_LINEIN)
HAVE_FMRADIO_REC_(|| global_settings.rec_source == AUDIO_SRC_FMRADIO)
)
{
for(i = 0; i < screen_update; i++) {
if (display_agc[i]) {
screens[i].puts(0, filename_offset[i] +
PM_HEIGHT + line[i], buf);
}
}
}
if (global_settings.rec_source == AUDIO_SRC_MIC)
{
if(agc_maxgain < (global_settings.rec_mic_gain))
change_recording_gain(false, true, true);
}
else
{
if(agc_maxgain < (global_settings.rec_left_gain))
change_recording_gain(false, true, false);
if(agc_maxgain < (global_settings.rec_right_gain))
change_recording_gain(false, false, true);
}
#else /* !HAVE_AGC */
}
#endif /* HAVE_AGC */
if(!global_settings.invert_cursor) {
switch(cursor)
{
case 1:
for(i = 0; i < screen_update; i++)
screen_put_cursorxy(&screens[i], 0,
filename_offset[i] +
PM_HEIGHT + 3, true);
if(global_settings.rec_source != AUDIO_SRC_MIC)
{
for(i = 0; i < screen_update; i++)
screen_put_cursorxy(&screens[i], 0,
filename_offset[i] +
PM_HEIGHT + 4, true);
}
break;
case 2:
for(i = 0; i < screen_update; i++)
screen_put_cursorxy(&screens[i], 0,
filename_offset[i] +
PM_HEIGHT + 3, true);
break;
case 3:
for(i = 0; i < screen_update; i++)
screen_put_cursorxy(&screens[i], 0,
filename_offset[i] +
PM_HEIGHT + 4, true);
break;
#ifdef HAVE_AGC
case 4:
case 5:
for(i = 0; i < screen_update; i++)
screen_put_cursorxy(&screens[i], 0,
filename_offset[i] +
PM_HEIGHT + line[i], true);
break;
#endif /* HAVE_AGC */
default:
for(i = 0; i < screen_update; i++)
screen_put_cursorxy(&screens[i], 0,
filename_offset[i] +
PM_HEIGHT + 2, true);
}
}
#ifdef HAVE_AGC
hist_time++;
#endif
for(i = 0; i < screen_update; i++)
{
gui_statusbar_draw(&(statusbars.statusbars[i]), true);
peak_meter_screen(&screens[i], 0, pm_y[i], h*PM_HEIGHT);
screens[i].update();
}
/* draw the trigger status */
FOR_NB_SCREENS(i)
{
trig_width[i] = ((screens[i].height < 64) ||
((screens[i].height < 72) && (PM_HEIGHT > 1))) ?
screens[i].width - 14 * w : screens[i].width;
trig_xpos[i] = screens[i].width - trig_width[i];
trig_ypos[i] = ((screens[i].height < 72) && (PM_HEIGHT > 1)) ?
h*2 :
h*(1 + filename_offset[i] + PM_HEIGHT + line[i]
#ifdef HAVE_AGC
+ 1
#endif
);
}
if (peak_meter_trigger_status() != TRIG_OFF)
{
peak_meter_draw_trig(trig_xpos, trig_ypos, trig_width,
screen_update);
for(i = 0; i < screen_update; i++){
screens[i].update_rect(trig_xpos[i], trig_ypos[i],
trig_width[i] + 2, TRIG_HEIGHT);
}
}
}
if(audio_stat & AUDIO_STATUS_ERROR)
{
done = true;
}
} /* end while(!done) */
audio_stat = audio_status();
if (audio_stat & AUDIO_STATUS_ERROR)
{
gui_syncsplash(0, str(LANG_SYSFONT_DISK_FULL));
gui_syncstatusbar_draw(&statusbars, true);
FOR_NB_SCREENS(i)
screens[i].update();
audio_error_clear();
while(1)
{
if (action_userabort(TIMEOUT_NOBLOCK))
break;
}
}
#if CONFIG_CODEC == SWCODEC
audio_stop_recording();
audio_close_recording();
#ifdef HAVE_FMRADIO_REC
if (radio_status != FMRADIO_OFF)
/* Restore radio playback - radio_status should be unchanged if started
through fm radio screen (barring usb connect) */
rec_set_source(AUDIO_SRC_FMRADIO, (radio_status & FMRADIO_PAUSED) ?
SRCF_FMRADIO_PAUSED : SRCF_FMRADIO_PLAYING);
else
#endif
/* Go back to playback mode */
rec_set_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
/* restore talk_menu setting */
talk_enable_menus();
#else /* !SWCODEC */
audio_init_playback();
#endif /* CONFIG_CODEC == SWCODEC */
/* make sure the trigger is really turned off */
peak_meter_trigger(false);
peak_meter_set_trigger_listener(NULL);
rec_status &= ~RCSTAT_IN_RECSCREEN;
sound_settings_apply();
FOR_NB_SCREENS(i)
screens[i].setfont(FONT_UI);
/* if the directory was created or recording happened, make sure the
browser is updated */
if (rec_status & (RCSTAT_CREATED_DIRECTORY | RCSTAT_HAVE_RECORDED))
reload_directory();
#if (CONFIG_LED == LED_REAL) && !defined(SIMULATOR)
ata_set_led_enabled(true);
#endif
settings_save();
return (rec_status & RCSTAT_BEEN_IN_USB_MODE) != 0;
} /* recording_screen */
#if CONFIG_KEYPAD == RECORDER_PAD
static bool f2_rec_screen(void)
{
static const char* const freq_str[6] =
{
"44.1kHz",
"48kHz",
"32kHz",
"22.05kHz",
"24kHz",
"16kHz"
};
bool exit = false;
bool used = false;
int w, h, i;
char buf[32];
int button;
struct audio_recording_options rec_options;
FOR_NB_SCREENS(i)
{
screens[i].setfont(FONT_SYSFIXED);
screens[i].getstringsize("A",&w,&h);
}
while (!exit) {
const char* ptr=NULL;
FOR_NB_SCREENS(i)
{
screens[i].clear_display();
/* Recording quality */
screens[i].putsxy(0, LCD_HEIGHT/2 - h*2,
str(LANG_SYSFONT_RECORDING_QUALITY));
}
snprintf(buf, sizeof(buf), "%d", global_settings.rec_quality);
FOR_NB_SCREENS(i)
{
screens[i].putsxy(0, LCD_HEIGHT/2-h, buf);
screens[i].mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8);
}
/* Frequency */
snprintf(buf, sizeof buf, "%s:", str(LANG_SYSFONT_RECORDING_FREQUENCY));
ptr = freq_str[global_settings.rec_frequency];
FOR_NB_SCREENS(i)
{
screens[i].getstringsize(buf,&w,&h);
screens[i].putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h*2, buf);
screens[i].getstringsize(ptr, &w, &h);
screens[i].putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h, ptr);
screens[i].mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8);
}
/* Channel mode */
switch ( global_settings.rec_channels ) {
case 0:
ptr = str(LANG_SYSFONT_CHANNEL_STEREO);
break;
case 1:
ptr = str(LANG_SYSFONT_CHANNEL_MONO);
break;
}
FOR_NB_SCREENS(i)
{
screens[i].getstringsize(str(LANG_SYSFONT_CHANNELS), &w, &h);
screens[i].putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h*2,
str(LANG_SYSFONT_CHANNELS));
screens[i].getstringsize(str(LANG_SYSFONT_MODE), &w, &h);
screens[i].putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 - h,
str(LANG_SYSFONT_MODE));
screens[i].getstringsize(ptr, &w, &h);
screens[i].putsxy(LCD_WIDTH - w, LCD_HEIGHT/2, ptr);
screens[i].mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
LCD_WIDTH/2 + 8, LCD_HEIGHT/2 - 4, 7, 8);
screens[i].update();
}
button = button_get(true);
switch (button) {
case BUTTON_LEFT:
case BUTTON_F2 | BUTTON_LEFT:
global_settings.rec_quality++;
if(global_settings.rec_quality > 7)
global_settings.rec_quality = 0;
used = true;
break;
case BUTTON_DOWN:
case BUTTON_F2 | BUTTON_DOWN:
global_settings.rec_frequency++;
if(global_settings.rec_frequency > 5)
global_settings.rec_frequency = 0;
used = true;
break;
case BUTTON_RIGHT:
case BUTTON_F2 | BUTTON_RIGHT:
global_settings.rec_channels++;
if(global_settings.rec_channels > 1)
global_settings.rec_channels = 0;
used = true;
break;
case BUTTON_F2 | BUTTON_REL:
if ( used )
exit = true;
used = true;
break;
case BUTTON_F2 | BUTTON_REPEAT:
used = true;
break;
default:
if(default_event_handler(button) == SYS_USB_CONNECTED)
return true;
break;
}
}
rec_init_recording_options(&rec_options);
rec_set_recording_options(&rec_options);
set_gain();
settings_save();
FOR_NB_SCREENS(i)
screens[i].setfont(FONT_UI);
return false;
}
static bool f3_rec_screen(void)
{
bool exit = false;
bool used = false;
int w, h, i;
int button;
char *src_str[] =
{
str(LANG_SYSFONT_RECORDING_SRC_MIC),
str(LANG_SYSFONT_LINE_IN),
str(LANG_SYSFONT_RECORDING_SRC_DIGITAL)
};
struct audio_recording_options rec_options;
FOR_NB_SCREENS(i)
{
screens[i].setfont(FONT_SYSFIXED);
screens[i].getstringsize("A",&w,&h);
}
while (!exit) {
char* ptr=NULL;
ptr = src_str[global_settings.rec_source];
FOR_NB_SCREENS(i)
{
screens[i].clear_display();
/* Recording source */
screens[i].putsxy(0, LCD_HEIGHT/2 - h*2,
str(LANG_SYSFONT_RECORDING_SOURCE));
screens[i].getstringsize(ptr, &w, &h);
screens[i].putsxy(0, LCD_HEIGHT/2-h, ptr);
screens[i].mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8);
}
/* trigger setup */
ptr = str(LANG_SYSFONT_RECORD_TRIGGER);
FOR_NB_SCREENS(i)
{
screens[i].getstringsize(ptr,&w,&h);
screens[i].putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h*2, ptr);
screens[i].mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8);
screens[i].update();
}
button = button_get(true);
switch (button) {
case BUTTON_DOWN:
case BUTTON_F3 | BUTTON_DOWN:
#ifndef SIMULATOR
rectrigger();
settings_apply_trigger();
#endif
exit = true;
break;
case BUTTON_LEFT:
case BUTTON_F3 | BUTTON_LEFT:
global_settings.rec_source++;
if(global_settings.rec_source > AUDIO_SRC_MAX)
global_settings.rec_source = 0;
used = true;
break;
case BUTTON_F3 | BUTTON_REL:
if ( used )
exit = true;
used = true;
break;
case BUTTON_F3 | BUTTON_REPEAT:
used = true;
break;
default:
if(default_event_handler(button) == SYS_USB_CONNECTED)
return true;
break;
}
}
rec_init_recording_options(&rec_options);
rec_set_recording_options(&rec_options);
set_gain();
settings_save();
FOR_NB_SCREENS(i)
screens[i].setfont(FONT_UI);
return false;
}
#endif /* CONFIG_KEYPAD == RECORDER_PAD */
#if CONFIG_CODEC == SWCODEC
void audio_beep(int duration)
{
/* dummy */
(void)duration;
}
#ifdef SIMULATOR
/* stubs for recording sim */
void audio_init_recording(unsigned int buffer_offset)
{
buffer_offset = buffer_offset;
}
void audio_close_recording(void)
{
}
unsigned long pcm_rec_get_warnings(void)
{
return 0;
}
unsigned long audio_recorded_time(void)
{
return 123;
}
unsigned long audio_num_recorded_bytes(void)
{
return 5 * 1024 * 1024;
}
void rec_set_source(int source, unsigned flags)
{
source = source;
flags = flags;
}
void audio_set_recording_options(struct audio_recording_options *options)
{
options = options;
}
void audio_set_recording_gain(int left, int right, int type)
{
left = left;
right = right;
type = type;
}
void audio_record(const char *filename)
{
filename = filename;
}
void audio_new_file(const char *filename)
{
filename = filename;
}
void audio_stop_recording(void)
{
}
void audio_pause_recording(void)
{
}
void audio_resume_recording(void)
{
}
#endif /* #ifdef SIMULATOR */
#endif /* #ifdef CONFIG_CODEC == SWCODEC */
#endif /* HAVE_RECORDING */