New recording feature: Prerecord up to 30 seconds before you press the Play key. Especially useful for FM radio recording. Also fixed a bug which didn't apply the recording settings correctly in the Radio screen.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4183 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Linus Nielsen Feltzing 2003-12-31 03:13:29 +00:00
parent 1c4ea83bba
commit 74976c1484
8 changed files with 565 additions and 284 deletions

View file

@ -1847,3 +1847,13 @@ id: LANG_FLIP_DISPLAY
desc: in settings_menu, option to turn display+buttos by 180 degreed desc: in settings_menu, option to turn display+buttos by 180 degreed
eng: "Upside Down" eng: "Upside Down"
new: new:
id: LANG_RECORD_PRERECORD
desc: in recording and radio screen
eng: "Prerecording"
new:
id: LANG_RECORD_PRERECORD_TIME
desc: in recording settings_menu
eng: "Prerecord time"
new:

View file

@ -172,9 +172,10 @@ bool radio_screen(void)
mpeg_set_recording_options(global_settings.rec_frequency, mpeg_set_recording_options(global_settings.rec_frequency,
global_settings.rec_quality, global_settings.rec_quality,
1 /* Line In */, 1, /* Line In */
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable); global_settings.rec_editable,
global_settings.rec_prerecord_time);
mpeg_set_recording_gain(mpeg_sound_default(SOUND_LEFT_GAIN), mpeg_set_recording_gain(mpeg_sound_default(SOUND_LEFT_GAIN),
@ -230,7 +231,7 @@ bool radio_screen(void)
switch(button) switch(button)
{ {
case BUTTON_OFF: case BUTTON_OFF:
if(mpeg_status()) if(mpeg_status() == MPEG_STATUS_RECORD)
{ {
mpeg_stop(); mpeg_stop();
status_set_playmode(STATUS_STOP); status_set_playmode(STATUS_STOP);
@ -244,17 +245,16 @@ bool radio_screen(void)
break; break;
case BUTTON_F3: case BUTTON_F3:
/* Only act if the mpeg is stopped */ if(mpeg_status() == MPEG_STATUS_RECORD)
if(!mpeg_status())
{ {
have_recorded = true; mpeg_new_file(rec_create_filename());
mpeg_record(rec_create_filename());
status_set_playmode(STATUS_RECORD);
update_screen = true; update_screen = true;
} }
else else
{ {
mpeg_new_file(rec_create_filename()); have_recorded = true;
mpeg_record(rec_create_filename());
status_set_playmode(STATUS_RECORD);
update_screen = true; update_screen = true;
} }
last_seconds = 0; last_seconds = 0;
@ -353,11 +353,12 @@ bool radio_screen(void)
case SYS_USB_CONNECTED: case SYS_USB_CONNECTED:
/* Only accept USB connection when not recording */ /* Only accept USB connection when not recording */
if(!mpeg_status()) if(mpeg_status() != MPEG_STATUS_RECORD)
{ {
usb_screen(); usb_screen();
fmradio_set_status(0); fmradio_set_status(0);
have_recorded = true; /* Refreshes the browser later on */ screen_freeze = true; /* Cosmetic: makes sure the
radio screen doesn't redraw */
done = true; done = true;
} }
break; break;
@ -368,6 +369,8 @@ bool radio_screen(void)
if(!screen_freeze) if(!screen_freeze)
{ {
lcd_setmargins(0, 8); lcd_setmargins(0, 8);
/* Only display the peak meter when not recording */
if(!mpeg_status()) if(!mpeg_status())
{ {
lcd_clearrect(0, 8 + fh*(top_of_screen + 3), LCD_WIDTH, fh); lcd_clearrect(0, 8 + fh*(top_of_screen + 3), LCD_WIDTH, fh);
@ -415,7 +418,7 @@ bool radio_screen(void)
str(LANG_CHANNEL_MONO)); str(LANG_CHANNEL_MONO));
lcd_puts(0, top_of_screen + 2, buf); lcd_puts(0, top_of_screen + 2, buf);
if(mpeg_status()) if(mpeg_status() == MPEG_STATUS_RECORD)
{ {
hours = seconds / 3600; hours = seconds / 3600;
minutes = (seconds - (hours * 3600)) / 60; minutes = (seconds - (hours * 3600)) / 60;
@ -424,6 +427,12 @@ bool radio_screen(void)
hours, minutes, seconds%60); hours, minutes, seconds%60);
lcd_puts(0, top_of_screen + 3, buf); lcd_puts(0, top_of_screen + 3, buf);
} }
else
{
snprintf(buf, 32, "%s %02d",
str(LANG_RECORD_PRERECORD), seconds%60);
lcd_puts(0, top_of_screen + 3, buf);
}
/* Only force the redraw if update_screen is true */ /* Only force the redraw if update_screen is true */
status_draw(update_screen); status_draw(update_screen);
@ -687,7 +696,19 @@ bool radio_delete_preset(void)
static bool fm_recording_settings(void) static bool fm_recording_settings(void)
{ {
return recording_menu(true); bool ret;
ret = recording_menu(true);
if(!ret)
{
mpeg_set_recording_options(global_settings.rec_frequency,
global_settings.rec_quality,
1, /* Line In */
global_settings.rec_channels,
global_settings.rec_editable,
global_settings.rec_prerecord_time);
}
return ret;
} }
bool radio_menu(void) bool radio_menu(void)

View file

@ -160,7 +160,8 @@ bool recording_screen(void)
global_settings.rec_quality, global_settings.rec_quality,
global_settings.rec_source, global_settings.rec_source,
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable); global_settings.rec_editable,
global_settings.rec_prerecord_time);
set_gain(); set_gain();
@ -174,7 +175,7 @@ bool recording_screen(void)
switch(button) switch(button)
{ {
case BUTTON_OFF: case BUTTON_OFF:
if(mpeg_status()) if(mpeg_status() & MPEG_STATUS_RECORD)
{ {
mpeg_stop(); mpeg_stop();
status_set_playmode(STATUS_STOP); status_set_playmode(STATUS_STOP);
@ -190,7 +191,7 @@ bool recording_screen(void)
case BUTTON_PLAY: case BUTTON_PLAY:
/* Only act if the mpeg is stopped */ /* Only act if the mpeg is stopped */
if(!mpeg_status()) if(!(mpeg_status() & MPEG_STATUS_RECORD))
{ {
have_recorded = true; have_recorded = true;
mpeg_record(rec_create_filename()); mpeg_record(rec_create_filename());
@ -305,13 +306,16 @@ bool recording_screen(void)
if (recording_menu(false)) if (recording_menu(false))
return SYS_USB_CONNECTED; return SYS_USB_CONNECTED;
settings_save(); settings_save();
mpeg_set_recording_options(global_settings.rec_frequency, mpeg_set_recording_options(global_settings.rec_frequency,
global_settings.rec_quality, global_settings.rec_quality,
global_settings.rec_source, global_settings.rec_source,
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable); global_settings.rec_editable,
global_settings.rec_prerecord_time);
set_gain(); set_gain();
update_countdown = 1; /* Update immediately */ update_countdown = 1; /* Update immediately */
lcd_setfont(FONT_SYSFIXED); lcd_setfont(FONT_SYSFIXED);
@ -319,7 +323,7 @@ bool recording_screen(void)
break; break;
case BUTTON_F2: case BUTTON_F2:
if(!mpeg_status()) if(mpeg_status() != MPEG_STATUS_RECORD)
{ {
if (f2_rec_screen()) if (f2_rec_screen())
{ {
@ -332,7 +336,7 @@ bool recording_screen(void)
break; break;
case BUTTON_F3: case BUTTON_F3:
if(!mpeg_status()) if(mpeg_status() != MPEG_STATUS_RECORD)
{ {
if (f3_rec_screen()) if (f3_rec_screen())
{ {
@ -346,14 +350,12 @@ bool recording_screen(void)
case SYS_USB_CONNECTED: case SYS_USB_CONNECTED:
/* Only accept USB connection when not recording */ /* Only accept USB connection when not recording */
if(!mpeg_status()) if(mpeg_status() != MPEG_STATUS_RECORD)
{ {
usb_screen(); usb_screen();
have_recorded = true; /* Refreshes the browser later on */ have_recorded = true; /* Refreshes the browser later on */
done = true; done = true;
} }
lcd_setfont(FONT_SYSFIXED);
lcd_setmargins(global_settings.invert_cursor ? 0 : w, 8);
break; break;
} }
@ -387,21 +389,28 @@ bool recording_screen(void)
dseconds = rec_timesplit_seconds(); dseconds = rec_timesplit_seconds();
/* Display the split interval if the record timesplit if(mpeg_status() & MPEG_STATUS_PRERECORD)
is active */
if (global_settings.rec_timesplit)
{ {
/* Display the record timesplit interval rather than snprintf(buf, 32, "%s...", str(LANG_RECORD_PRERECORD));
the file size if the record timer is active */
dhours = dseconds / 3600;
dminutes = (dseconds - (dhours * 3600)) / 60;
snprintf(buf, 32, "%s %02d:%02d",
str(LANG_RECORD_TIMESPLIT_REC),
dhours, dminutes);
} }
else else
snprintf(buf, 32, "%s %s", str(LANG_RECORDING_SIZE), {
num2max5(mpeg_num_recorded_bytes(), buf2)); /* Display the split interval if the record timesplit
is active */
if (global_settings.rec_timesplit)
{
/* 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, 32, "%s %02d:%02d",
str(LANG_RECORD_TIMESPLIT_REC),
dhours, dminutes);
}
else
snprintf(buf, 32, "%s %s", str(LANG_RECORDING_SIZE),
num2max5(mpeg_num_recorded_bytes(), buf2));
}
lcd_puts(0, 1, buf); lcd_puts(0, 1, buf);
/* We will do file splitting regardless, since the OFF /* We will do file splitting regardless, since the OFF
@ -622,7 +631,8 @@ bool f2_rec_screen(void)
global_settings.rec_quality, global_settings.rec_quality,
global_settings.rec_source, global_settings.rec_source,
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable); global_settings.rec_editable,
global_settings.rec_prerecord_time);
set_gain(); set_gain();
@ -671,22 +681,6 @@ bool f3_rec_screen(void)
used = true; used = true;
break; break;
case BUTTON_DOWN:
case BUTTON_F3 | 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_F3 | BUTTON_RIGHT:
global_settings.rec_channels++;
if(global_settings.rec_channels > 1)
global_settings.rec_channels = 0;
used = true;
break;
case BUTTON_F3 | BUTTON_REL: case BUTTON_F3 | BUTTON_REL:
if ( used ) if ( used )
exit = true; exit = true;
@ -707,10 +701,11 @@ bool f3_rec_screen(void)
global_settings.rec_quality, global_settings.rec_quality,
global_settings.rec_source, global_settings.rec_source,
global_settings.rec_channels, global_settings.rec_channels,
global_settings.rec_editable); global_settings.rec_editable,
global_settings.rec_prerecord_time);
set_gain(); set_gain();
settings_save(); settings_save();
lcd_setfont(FONT_UI); lcd_setfont(FONT_UI);

View file

@ -129,6 +129,9 @@ location used, and reset the setting in question with a factory default if
needed. Memory locations not used by a given version should not be needed. Memory locations not used by a given version should not be
modified unless the header & checksum test fails. modified unless the header & checksum test fails.
Because 0xff mean that the byte is unused, care must be taken so that
a used byte can't have the value 0xff. Either use only 7 bits, or make sure
that the value will never be 0xff.
Rest of config block, only saved to disk: Rest of config block, only saved to disk:
0xA8 (char)jump scroll mode (only for player) 0xA8 (char)jump scroll mode (only for player)
@ -151,9 +154,8 @@ Rest of config block, only saved to disk:
0xB8 (char[20]) WPS file 0xB8 (char[20]) WPS file
0xCC (char[20]) Lang file 0xCC (char[20]) Lang file
0xE0 (char[20]) Font file 0xE0 (char[20]) Font file
0xF4 <unused> 0xF4 Prerecording time (bit 0-4)
0xF8 <unused> 0xF5-0xFF <unused>
0xFC <unused>
*************************************/ *************************************/
@ -432,6 +434,8 @@ int settings_save( void )
strncpy(&config_block[0xcc], global_settings.lang_file, MAX_FILENAME); strncpy(&config_block[0xcc], global_settings.lang_file, MAX_FILENAME);
strncpy(&config_block[0xe0], global_settings.font_file, MAX_FILENAME); strncpy(&config_block[0xe0], global_settings.font_file, MAX_FILENAME);
config_block[0xf4]=(unsigned char)global_settings.rec_prerecord_time;
if(save_config_buffer()) if(save_config_buffer())
{ {
lcd_clear_display(); lcd_clear_display();
@ -756,6 +760,10 @@ void settings_load(void)
strncpy(global_settings.wps_file, &config_block[0xb8], MAX_FILENAME); strncpy(global_settings.wps_file, &config_block[0xb8], MAX_FILENAME);
strncpy(global_settings.lang_file, &config_block[0xcc], MAX_FILENAME); strncpy(global_settings.lang_file, &config_block[0xcc], MAX_FILENAME);
strncpy(global_settings.font_file, &config_block[0xe0], MAX_FILENAME); strncpy(global_settings.font_file, &config_block[0xe0], MAX_FILENAME);
if (config_block[0xf4] != 0xff)
global_settings.rec_prerecord_time = config_block[0xf4];
#ifdef HAVE_LCD_CHARCELLS #ifdef HAVE_LCD_CHARCELLS
if (config_block[0xa8] != 0xff) if (config_block[0xa8] != 0xff)
global_settings.jump_scroll = config_block[0xa8]; global_settings.jump_scroll = config_block[0xa8];
@ -1079,6 +1087,9 @@ bool settings_load_config(char* file)
else if (!strcasecmp(name, "editable recordings")) { else if (!strcasecmp(name, "editable recordings")) {
set_cfg_bool(&global_settings.rec_editable, value); set_cfg_bool(&global_settings.rec_editable, value);
} }
else if (!strcasecmp(name, "prerecording time")) {
set_cfg_int(&global_settings.rec_prerecord_time, value, 0, 30);
}
#endif #endif
else if (!strcasecmp(name, "idle poweroff")) { else if (!strcasecmp(name, "idle poweroff")) {
static char* options[] = {"off","1","2","3","4","5","6","7","8", static char* options[] = {"off","1","2","3","4","5","6","7","8",
@ -1379,6 +1390,10 @@ bool settings_save_config(void)
fprintf(fd, "line in: %s\r\n", boolopt[global_settings.line_in]); fprintf(fd, "line in: %s\r\n", boolopt[global_settings.line_in]);
#endif #endif
fprintf(fd, "max files in dir: %d\r\n", global_settings.max_files_in_dir);
fprintf(fd, "max files in playlist: %d\r\n",
global_settings.max_files_in_playlist);
#ifdef HAVE_MAS3587F #ifdef HAVE_MAS3587F
fprintf(fd, "#\r\n# Recording\r\n#\r\n"); fprintf(fd, "#\r\n# Recording\r\n#\r\n");
fprintf(fd, "rec quality: %d\r\n", global_settings.rec_quality); fprintf(fd, "rec quality: %d\r\n", global_settings.rec_quality);
@ -1409,12 +1424,12 @@ bool settings_save_config(void)
fprintf(fd, "editable recordings: %s\r\n", fprintf(fd, "editable recordings: %s\r\n",
boolopt[global_settings.rec_editable]); boolopt[global_settings.rec_editable]);
fprintf(fd, "prerecording time: %d\r\n",
global_settings.rec_prerecord_time);
#endif #endif
fprintf(fd, "max files in dir: %d\r\n", global_settings.max_files_in_dir); fprintf(fd, "#\r\n# Playlists\r\n#\r\n");
fprintf(fd, "max files in playlist: %d\r\n",
global_settings.max_files_in_playlist);
{ {
static char* options[] = {"off", "on", "ask"}; static char* options[] = {"off", "on", "ask"};
fprintf(fd, "recursive directory insert: %s\r\n", fprintf(fd, "recursive directory insert: %s\r\n",
@ -1454,6 +1469,7 @@ void settings_reset(void) {
global_settings.rec_left_gain = 2; /* 0dB */ global_settings.rec_left_gain = 2; /* 0dB */
global_settings.rec_right_gain = 2; /* 0dB */ global_settings.rec_right_gain = 2; /* 0dB */
global_settings.rec_editable = false; global_settings.rec_editable = false;
global_settings.rec_prerecord_time = 0;
global_settings.resume = RESUME_ASK; global_settings.resume = RESUME_ASK;
global_settings.contrast = lcd_default_contrast(); global_settings.contrast = lcd_default_contrast();
global_settings.invert = DEFAULT_INVERT_SETTING; global_settings.invert = DEFAULT_INVERT_SETTING;

View file

@ -85,6 +85,8 @@ struct user_settings
5 = 01:00, 6 = 02:00, 7 = 04:00, 8 = 06:00 5 = 01:00, 6 = 02:00, 7 = 04:00, 8 = 06:00
9 = 08:00, 10= 10:00, 11= 12:00, 12= 18:00, 9 = 08:00, 10= 10:00, 11= 12:00, 12= 18:00,
13= 24:00 */ 13= 24:00 */
int rec_prerecord_time; /* In seconds, 0-30, 0 means OFF */
/* device settings */ /* device settings */

View file

@ -241,6 +241,20 @@ static bool rectimesplit(void)
names, 14, NULL ); names, 14, NULL );
} }
static bool recprerecord(void)
{
char *names[] = {
str(LANG_OFF),"1s","2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s",
"10s", "11s", "12s", "13s", "14s", "15s", "16s", "17s", "18s", "19s",
"20s", "21s", "22s", "23s", "24s", "25s", "26s", "27s", "28s", "29s",
"30s"
};
return set_option(str(LANG_RECORD_PRERECORD_TIME),
&global_settings.rec_prerecord_time, INT,
names, 31, NULL );
}
#endif /* HAVE_MAS3587F */ #endif /* HAVE_MAS3587F */
static void set_chanconf(int val) static void set_chanconf(int val)
@ -294,7 +308,7 @@ bool recording_menu(bool no_source)
{ {
int m; int m;
int i = 0; int i = 0;
struct menu_items menu[6]; struct menu_items menu[7];
bool result; bool result;
menu[i].desc = str(LANG_RECORDING_QUALITY); menu[i].desc = str(LANG_RECORDING_QUALITY);
@ -311,6 +325,8 @@ bool recording_menu(bool no_source)
menu[i++].function = receditable; menu[i++].function = receditable;
menu[i].desc = str(LANG_RECORD_TIMESPLIT); menu[i].desc = str(LANG_RECORD_TIMESPLIT);
menu[i++].function = rectimesplit; menu[i++].function = rectimesplit;
menu[i].desc = str(LANG_RECORD_PRERECORD_TIME);
menu[i++].function = recprerecord;
m=menu_init( menu, i ); m=menu_init( menu, i );
result = menu_run(m); result = menu_run(m);

View file

@ -32,6 +32,8 @@
#define MPEG_PLAY_PENDING_THRESHOLD 0x10000 #define MPEG_PLAY_PENDING_THRESHOLD 0x10000
#define MPEG_PLAY_PENDING_SWAPSIZE 0x10000 #define MPEG_PLAY_PENDING_SWAPSIZE 0x10000
#define MPEG_MAX_PRERECORD_SECONDS 30
/* For ID3 info and VBR header */ /* For ID3 info and VBR header */
#define MPEG_RESERVED_HEADER_SPACE (4096 + 1500) #define MPEG_RESERVED_HEADER_SPACE (4096 + 1500)
@ -89,7 +91,7 @@ void mpeg_record(char *filename);
void mpeg_new_file(char *filename); void mpeg_new_file(char *filename);
void mpeg_set_recording_options(int frequency, int quality, void mpeg_set_recording_options(int frequency, int quality,
int source, int channel_mode, int source, int channel_mode,
bool editable); bool editable, int prerecord_time);
void mpeg_set_recording_gain(int left, int right, bool use_mic); void mpeg_set_recording_gain(int left, int right, bool use_mic);
unsigned long mpeg_recorded_time(void); unsigned long mpeg_recorded_time(void);
unsigned long mpeg_num_recorded_bytes(void); unsigned long mpeg_num_recorded_bytes(void);
@ -123,7 +125,8 @@ void mpeg_error_clear(void);
#define MPEG_STATUS_PLAY 1 #define MPEG_STATUS_PLAY 1
#define MPEG_STATUS_PAUSE 2 #define MPEG_STATUS_PAUSE 2
#define MPEG_STATUS_RECORD 4 #define MPEG_STATUS_RECORD 4
#define MPEG_STATUS_ERROR 8 #define MPEG_STATUS_PRERECORD 8
#define MPEG_STATUS_ERROR 16
#define MPEGERR_DISK_FULL 1 #define MPEGERR_DISK_FULL 1

View file

@ -44,6 +44,7 @@ extern void bitswap(unsigned char *data, int length);
#ifdef HAVE_MAS3587F #ifdef HAVE_MAS3587F
static void init_recording(void); static void init_recording(void);
static void init_playback(void); static void init_playback(void);
static void start_prerecording(void);
static void start_recording(void); static void start_recording(void);
static void stop_recording(void); static void stop_recording(void);
static int get_unsaved_space(void); static int get_unsaved_space(void);
@ -497,6 +498,17 @@ static char recording_filename[MAX_PATH];
static int rec_frequency_index; /* For create_xing_header() calls */ static int rec_frequency_index; /* For create_xing_header() calls */
static int rec_version_index; /* For create_xing_header() calls */ static int rec_version_index; /* For create_xing_header() calls */
static bool disable_xing_header; /* When splitting files */ static bool disable_xing_header; /* When splitting files */
static bool prerecording; /* True if prerecording is enabled */
static bool is_prerecording; /* True if we are prerecording */
static int prerecord_buffer[MPEG_MAX_PRERECORD_SECONDS]; /* Array of buffer
indexes for each
prerecorded
second */
static int prerecord_index; /* Current index in the prerecord buffer */
static int prerecording_max_seconds; /* Max number of seconds to store */
static int prerecord_count; /* Number of seconds in the prerecord buffer */
static int prerecord_timeout; /* The tick count of the next prerecord data store */
#endif #endif
static int mpeg_file; static int mpeg_file;
@ -865,17 +877,38 @@ static void dma_tick(void)
num_rec_bytes += i; num_rec_bytes += i;
/* Signal to save the data if we are running out of buffer if(is_prerecording)
space */
num_bytes = mp3buf_write - mp3buf_read;
if(num_bytes < 0)
num_bytes += mp3buflen;
if(mp3buflen - num_bytes < MPEG_RECORDING_LOW_WATER && !saving)
{ {
saving = true; if(TIME_AFTER(current_tick, prerecord_timeout))
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); {
wake_up_thread(); prerecord_timeout = current_tick + HZ;
/* Store the write pointer every second */
prerecord_buffer[prerecord_index++] = mp3buf_write;
/* Wrap if necessary */
if(prerecord_index == prerecording_max_seconds)
prerecord_index = 0;
/* Update the number of seconds recorded */
if(prerecord_count < prerecording_max_seconds)
prerecord_count++;
}
}
else
{
/* Signal to save the data if we are running out of buffer
space */
num_bytes = mp3buf_write - mp3buf_read;
if(num_bytes < 0)
num_bytes += mp3buflen;
if(mp3buflen - num_bytes < MPEG_RECORDING_LOW_WATER && !saving)
{
saving = true;
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
wake_up_thread();
}
} }
} }
} }
@ -1887,20 +1920,84 @@ static void mpeg_thread(void)
switch(ev.id) switch(ev.id)
{ {
case MPEG_RECORD: case MPEG_RECORD:
DEBUGF("Recording...\n"); if(is_prerecording)
reset_mp3_buffer(); {
int startpos, i;
/* Go back prerecord_count seconds in the buffer */
startpos = prerecord_index - prerecord_count;
if(startpos < 0)
startpos += prerecording_max_seconds;
/* Advance the write pointer to make /* Read the mp3 buffer pointer from the prerecord buffer */
room for an ID3 tag plus a VBR header */ startpos = prerecord_buffer[startpos];
mp3buf_write = MPEG_RESERVED_HEADER_SPACE;
memset(mp3buf, 0, MPEG_RESERVED_HEADER_SPACE);
/* Insert the ID3 header */ DEBUGF("Start looking at address %x (%x)\n",
memcpy(mp3buf, empty_id3_header, sizeof(empty_id3_header)); mp3buf+startpos, startpos);
saved_header = get_last_recorded_header();
mem_find_next_frame(startpos, &offset, 5000,
saved_header);
mp3buf_read = startpos + offset;
DEBUGF("New mp3buf_read address: %x (%x)\n",
mp3buf+mp3buf_read, mp3buf_read);
/* Make room for headers */
mp3buf_read -= MPEG_RESERVED_HEADER_SPACE;
if(mp3buf_read < 0)
{
/* Clear the bottom half */
memset(mp3buf, 0,
mp3buf_read + MPEG_RESERVED_HEADER_SPACE);
/* And the top half */
mp3buf_read += mp3buflen;
memset(mp3buf + mp3buf_read, 0,
mp3buflen - mp3buf_read);
}
else
{
memset(mp3buf + mp3buf_read, 0,
MPEG_RESERVED_HEADER_SPACE);
}
/* Copy the empty ID3 header */
startpos = mp3buf_read;
for(i = 0;i < (int)sizeof(empty_id3_header);i++)
{
mp3buf[startpos++] = empty_id3_header[i];
if(startpos == mp3buflen)
startpos = 0;
}
DEBUGF("New mp3buf_read address (reservation): %x\n",
mp3buf+mp3buf_read);
DEBUGF("Prerecording...\n");
}
else
{
reset_mp3_buffer();
num_rec_bytes = 0;
/* Advance the write pointer to make
room for an ID3 tag plus a VBR header */
mp3buf_write = MPEG_RESERVED_HEADER_SPACE;
memset(mp3buf, 0, MPEG_RESERVED_HEADER_SPACE);
/* Insert the ID3 header */
memcpy(mp3buf, empty_id3_header,
sizeof(empty_id3_header));
DEBUGF("Recording...\n");
}
start_recording(); start_recording();
demand_irq_enable(true);
mpeg_file = open(recording_filename, O_WRONLY|O_CREAT); mpeg_file = open(recording_filename, O_WRONLY|O_CREAT);
if(mpeg_file < 0) if(mpeg_file < 0)
@ -1910,14 +2007,13 @@ static void mpeg_thread(void)
case MPEG_STOP: case MPEG_STOP:
DEBUGF("MPEG_STOP\n"); DEBUGF("MPEG_STOP\n");
demand_irq_enable(false);
/* Store the last recorded header for later use by the /* Store the last recorded header for later use by the
Xing header generation */ Xing header generation */
saved_header = get_last_recorded_header(); saved_header = get_last_recorded_header();
stop_recording(); stop_recording();
/* Save the remaining data in the buffer */ /* Save the remaining data in the buffer */
stop_pending = true; stop_pending = true;
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
@ -1942,6 +2038,11 @@ static void mpeg_thread(void)
if(num_recorded_frames == 0x7ffff) if(num_recorded_frames == 0x7ffff)
num_recorded_frames = 0; num_recorded_frames = 0;
/* Also, if we have been prerecording, the frame count
will be wrong */
if(prerecording)
num_recorded_frames = 0;
/* saved_header is saved right before stopping /* saved_header is saved right before stopping
the MAS */ the MAS */
framelen = create_xing_header(mpeg_file, 0, framelen = create_xing_header(mpeg_file, 0,
@ -1970,6 +2071,11 @@ static void mpeg_thread(void)
} }
} }
#endif #endif
if(prerecording)
{
start_prerecording();
}
mpeg_stop_done = true; mpeg_stop_done = true;
break; break;
@ -2117,7 +2223,6 @@ static void mpeg_thread(void)
if(errno == ENOSPC) if(errno == ENOSPC)
{ {
mpeg_errno = MPEGERR_DISK_FULL; mpeg_errno = MPEGERR_DISK_FULL;
demand_irq_enable(false);
stop_recording(); stop_recording();
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0); queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
break; break;
@ -2161,18 +2266,22 @@ static void mpeg_thread(void)
break; break;
case SYS_USB_CONNECTED: case SYS_USB_CONNECTED:
is_playing = false; /* We can safely go to USB mode if no recording
paused = false; is taking place */
stop_playing(); if((!is_recording || is_prerecording) && mpeg_stop_done)
#ifndef SIMULATOR {
/* Even if we aren't recording, we still call this
function, to put the MAS in monitoring mode,
to save power. */
stop_recording();
/* Tell the USB thread that we are safe */ /* Tell the USB thread that we are safe */
DEBUGF("mpeg_thread got SYS_USB_CONNECTED\n"); DEBUGF("mpeg_thread got SYS_USB_CONNECTED\n");
usb_acknowledge(SYS_USB_CONNECTED_ACK); usb_acknowledge(SYS_USB_CONNECTED_ACK);
/* Wait until the USB cable is extracted again */ /* Wait until the USB cable is extracted again */
usb_wait_for_disconnect(&mpeg_queue); usb_wait_for_disconnect(&mpeg_queue);
#endif }
break; break;
} }
} }
@ -2245,16 +2354,6 @@ bool mpeg_has_changed_track(void)
} }
#ifdef HAVE_MAS3587F #ifdef HAVE_MAS3587F
void mpeg_init_recording(void)
{
init_recording_done = false;
queue_post(&mpeg_queue, MPEG_INIT_RECORDING, NULL);
while(!init_recording_done)
sleep_thread();
wake_up_thread();
}
void mpeg_init_playback(void) void mpeg_init_playback(void)
{ {
init_playback_done = false; init_playback_done = false;
@ -2265,99 +2364,14 @@ void mpeg_init_playback(void)
wake_up_thread(); wake_up_thread();
} }
static void init_recording(void)
{
unsigned long val;
int rc;
stop_playing();
is_playing = false;
paused = false;
reset_mp3_buffer();
remove_all_tags();
if(mpeg_file >= 0)
close(mpeg_file);
mpeg_file = -1;
/* Init the recording variables */
is_recording = false;
mas_reset();
/* Enable the audio CODEC and the DSP core, max analog voltage range */
rc = mas_direct_config_write(MAS_CONTROL, 0x8c00);
if(rc < 0)
panicf("mas_ctrl_w: %d", rc);
/* Stop the current application */
val = 0;
mas_writemem(MAS_BANK_D0,0x7f6,&val,1);
do
{
mas_readmem(MAS_BANK_D0, 0x7f7, &val, 1);
} while(val);
/* Perform black magic as described by the data sheet */
if((mas_version_code & 0xff) == 2)
{
DEBUGF("Performing MAS black magic for B2 version\n");
mas_writereg(0xa3, 0x98);
mas_writereg(0x94, 0xfffff);
val = 0;
mas_writemem(MAS_BANK_D1, 0, &val, 1);
mas_writereg(0xa3, 0x90);
}
/* Enable A/D Converters */
mas_codec_writereg(0x0, 0xcccd);
/* Copy left channel to right (mono mode) */
mas_codec_writereg(8, 0x8000);
/* ADC scale 0%, DSP scale 100%
We use the DSP output for monitoring, because it works with all
sources including S/PDIF */
mas_codec_writereg(6, 0x0000);
mas_codec_writereg(7, 0x4000);
/* No mute */
val = 0;
mas_writemem(MAS_BANK_D0, 0x7f9, &val, 1);
/* Set Demand mode, no monitoring and validate all settings */
val = 0x125;
mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1);
/* Start the encoder application */
val = 0x40;
mas_writemem(MAS_BANK_D0, 0x7f6, &val, 1);
do
{
mas_readmem(MAS_BANK_D0, 0x7f7, &val, 1);
} while(!(val & 0x40));
/* We have started the recording application with monitoring OFF.
This is because we want to record at least one frame to fill the DMA
buffer, because the silly MAS will not negate EOD until at least one
DMA transfer has taken place.
Now let's wait for some data to be encoded. */
sleep(20);
/* Disable IRQ6 */
IPRB &= 0xff0f;
mpeg_mode = MPEG_ENCODER;
DEBUGF("MAS Recording application started\n");
}
static void init_playback(void) static void init_playback(void)
{ {
unsigned long val; unsigned long val;
int rc; int rc;
if(mpeg_mode == MPEG_ENCODER)
stop_recording();
stop_dma(); stop_dma();
mas_reset(); mas_reset();
@ -2411,6 +2425,121 @@ static void init_playback(void)
DEBUGF("MAS Decoding application started\n"); DEBUGF("MAS Decoding application started\n");
} }
/****************************************************************************
**
**
** Recording functions
**
**
***************************************************************************/
void mpeg_init_recording(void)
{
init_recording_done = false;
queue_post(&mpeg_queue, MPEG_INIT_RECORDING, NULL);
while(!init_recording_done)
sleep_thread();
wake_up_thread();
}
static void init_recording(void)
{
unsigned long val;
int rc;
/* Disable IRQ6 */
IPRB &= 0xff0f;
stop_playing();
is_playing = false;
paused = false;
reset_mp3_buffer();
remove_all_tags();
if(mpeg_file >= 0)
close(mpeg_file);
mpeg_file = -1;
/* Init the recording variables */
is_recording = false;
is_prerecording = false;
mpeg_stop_done = true;
mas_reset();
/* Enable the audio CODEC and the DSP core, max analog voltage range */
rc = mas_direct_config_write(MAS_CONTROL, 0x8c00);
if(rc < 0)
panicf("mas_ctrl_w: %d", rc);
/* Stop the current application */
val = 0;
mas_writemem(MAS_BANK_D0,0x7f6,&val,1);
do
{
mas_readmem(MAS_BANK_D0, 0x7f7, &val, 1);
} while(val);
/* Perform black magic as described by the data sheet */
if((mas_version_code & 0xff) == 2)
{
DEBUGF("Performing MAS black magic for B2 version\n");
mas_writereg(0xa3, 0x98);
mas_writereg(0x94, 0xfffff);
val = 0;
mas_writemem(MAS_BANK_D1, 0, &val, 1);
mas_writereg(0xa3, 0x90);
}
/* Enable A/D Converters */
mas_codec_writereg(0x0, 0xcccd);
/* Copy left channel to right (mono mode) */
mas_codec_writereg(8, 0x8000);
/* ADC scale 0%, DSP scale 100%
We use the DSP output for monitoring, because it works with all
sources including S/PDIF */
mas_codec_writereg(6, 0x0000);
mas_codec_writereg(7, 0x4000);
/* No mute */
val = 0;
mas_writemem(MAS_BANK_D0, 0x7f9, &val, 1);
/* Set Demand mode, no monitoring and validate all settings */
val = 0x125;
mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1);
/* Start the encoder application */
val = 0x40;
mas_writemem(MAS_BANK_D0, 0x7f6, &val, 1);
do
{
mas_readmem(MAS_BANK_D0, 0x7f7, &val, 1);
} while(!(val & 0x40));
/* We have started the recording application with monitoring OFF.
This is because we want to record at least one frame to fill the DMA
buffer, because the silly MAS will not negate EOD until at least one
DMA transfer has taken place.
Now let's wait for some data to be encoded. */
sleep(20);
/* Now set it to Monitoring mode as default, saves power */
val = 0x525;
mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1);
mpeg_mode = MPEG_ENCODER;
DEBUGF("MAS Recording application started\n");
/* At this point, all settings are the reset MAS defaults, next thing is to
call mpeg_set_recording_options(). */
}
void mpeg_record(char *filename) void mpeg_record(char *filename)
{ {
mpeg_errno = 0; mpeg_errno = 0;
@ -2418,53 +2547,109 @@ void mpeg_record(char *filename)
strncpy(recording_filename, filename, MAX_PATH - 1); strncpy(recording_filename, filename, MAX_PATH - 1);
recording_filename[MAX_PATH - 1] = 0; recording_filename[MAX_PATH - 1] = 0;
num_rec_bytes = 0;
disable_xing_header = false; disable_xing_header = false;
queue_post(&mpeg_queue, MPEG_RECORD, NULL); queue_post(&mpeg_queue, MPEG_RECORD, NULL);
} }
static void start_prerecording(void)
{
unsigned long val;
DEBUGF("Starting prerecording\n");
prerecord_index = 0;
prerecord_count = 0;
prerecord_timeout = current_tick + HZ;
memset(prerecord_buffer, 0, sizeof(prerecord_buffer));
reset_mp3_buffer();
is_prerecording = true;
/* Stop monitoring and start the encoder */
mas_readmem(MAS_BANK_D0, 0x7f1, &val, 1);
val &= ~(1 << 10);
val |= 1;
mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1);
DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f1, %x)\n", val);
/* Wait until the DSP has accepted the settings */
do
{
mas_readmem(MAS_BANK_D0, 0x7f1, &val,1);
} while(val & 1);
sleep(20);
is_recording = true;
stop_pending = false;
saving = false;
demand_irq_enable(true);
}
static void start_recording(void) static void start_recording(void)
{ {
unsigned long val; unsigned long val;
num_recorded_frames = 0; num_recorded_frames = 0;
/* Stop monitoring and record for real */
mas_readmem(MAS_BANK_D0, 0x7f1, &val, 1);
val &= ~(1 << 10);
val |= 1;
mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1);
/* Wait until the DSP has accepted the settings */ if(is_prerecording)
do
{ {
mas_readmem(MAS_BANK_D0, 0x7f1, &val,1); /* This will make the IRQ handler start recording
} while(val & 1); for real, i.e send MPEG_SAVE_DATA messages when
the buffer is full */
sleep(20); is_prerecording = false;
}
else
{
/* If prerecording is off, we need to stop the monitoring
and start the encoder */
mas_readmem(MAS_BANK_D0, 0x7f1, &val, 1);
val &= ~(1 << 10);
val |= 1;
mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1);
DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f1, %x)\n", val);
/* Wait until the DSP has accepted the settings */
do
{
mas_readmem(MAS_BANK_D0, 0x7f1, &val,1);
} while(val & 1);
sleep(20);
}
/* Store the current time */
record_start_time = current_tick;
is_recording = true; is_recording = true;
stop_pending = false; stop_pending = false;
saving = false; saving = false;
/* Store the current time */
if(prerecording)
record_start_time = current_tick - prerecord_count * HZ;
else
record_start_time = current_tick;
demand_irq_enable(true);
} }
static void stop_recording(void) static void stop_recording(void)
{ {
unsigned long val; unsigned long val;
is_recording = false; demand_irq_enable(false);
is_recording = false;
is_prerecording = false;
/* Read the number of frames recorded */ /* Read the number of frames recorded */
mas_readmem(MAS_BANK_D0, 0xfd0, &num_recorded_frames, 1); mas_readmem(MAS_BANK_D0, 0xfd0, &num_recorded_frames, 1);
/* Start monitoring */ /* Start monitoring */
mas_readmem(MAS_BANK_D0, 0x7f1, &val, 1); mas_readmem(MAS_BANK_D0, 0x7f1, &val, 1);
val |= (1 << 10) | 1; val |= (1 << 10) | 1;
mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1); mas_writemem(MAS_BANK_D0, 0x7f1, &val, 1);
DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f1, %x)\n", val);
/* Wait until the DSP has accepted the settings */ /* Wait until the DSP has accepted the settings */
do do
{ {
@ -2474,6 +2659,82 @@ static void stop_recording(void)
drain_dma_buffer(); drain_dma_buffer();
} }
void mpeg_set_recording_options(int frequency, int quality,
int source, int channel_mode,
bool editable, int prerecord_time)
{
bool is_mpeg1;
unsigned long val;
is_mpeg1 = (frequency < 3)?true:false;
rec_version_index = is_mpeg1?3:2;
rec_frequency_index = frequency % 3;
val = (quality << 17) |
(rec_frequency_index << 10) |
((is_mpeg1?1:0) << 9) |
(1 << 8) | /* CRC on */
(((channel_mode * 2 + 1) & 3) << 6) |
(1 << 5) /* MS-stereo */ |
(1 << 2) /* Is an original */;
mas_writemem(MAS_BANK_D0, 0x7f0, &val,1);
DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f0, %x)\n", val);
val = editable?4:0;
mas_writemem(MAS_BANK_D0, 0x7f9, &val,1);
DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f9, %x)\n", val);
val = (((source < 2)?1:2) << 8) | /* Input select */
(1 << 5) | /* SDO strobe invert */
((is_mpeg1?0:1) << 3) |
(1 << 2) | /* Inverted SIBC clock signal */
1; /* Validate */
mas_writemem(MAS_BANK_D0, 0x7f1, &val,1);
DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f1, %x)\n", val);
drain_dma_buffer();
if(source == 0) /* Mic */
{
/* Copy left channel to right (mono mode) */
mas_codec_writereg(8, 0x8000);
}
else
{
/* Stereo input mode */
mas_codec_writereg(8, 0);
}
prerecording_max_seconds = prerecord_time;
if(prerecording_max_seconds)
{
prerecording = true;
start_prerecording();
}
else
{
prerecording = false;
is_prerecording = false;
is_recording = false;
}
}
/* If use_mic is true, the left gain is used */
void mpeg_set_recording_gain(int left, int right, bool use_mic)
{
/* Enable both left and right A/D */
mas_codec_writereg(0x0,
(left << 12) |
(right << 8) |
(left << 4) |
(use_mic?0x0008:0) | /* Connect left A/D to mic */
0x0007);
}
void mpeg_new_file(char *filename) void mpeg_new_file(char *filename)
{ {
mpeg_errno = 0; mpeg_errno = 0;
@ -2492,16 +2753,37 @@ void mpeg_new_file(char *filename)
unsigned long mpeg_recorded_time(void) unsigned long mpeg_recorded_time(void)
{ {
if(is_prerecording)
return prerecord_count * HZ;
if(is_recording) if(is_recording)
return current_tick - record_start_time; return current_tick - record_start_time;
else
return 0; return 0;
} }
unsigned long mpeg_num_recorded_bytes(void) unsigned long mpeg_num_recorded_bytes(void)
{ {
int num_bytes;
int index;
if(is_recording) if(is_recording)
return num_rec_bytes; {
if(is_prerecording)
{
index = prerecord_index - prerecord_count;
if(index < 0)
index += prerecording_max_seconds;
num_bytes = mp3buf_write - prerecord_buffer[index];
if(num_bytes < 0)
num_bytes += mp3buflen;
return num_bytes;;
}
else
return num_rec_bytes;
}
else else
return 0; return 0;
} }
@ -2660,8 +2942,11 @@ int mpeg_status(void)
ret |= MPEG_STATUS_PAUSE; ret |= MPEG_STATUS_PAUSE;
#ifdef HAVE_MAS3587F #ifdef HAVE_MAS3587F
if(is_recording) if(is_recording && !is_prerecording)
ret |= MPEG_STATUS_RECORD; ret |= MPEG_STATUS_RECORD;
if(is_prerecording)
ret |= MPEG_STATUS_PRERECORD;
#endif #endif
if(mpeg_errno) if(mpeg_errno)
@ -3057,73 +3342,6 @@ void mpeg_set_pitch(int pitch)
} }
#endif #endif
#ifdef HAVE_MAS3587F
void mpeg_set_recording_options(int frequency, int quality,
int source, int channel_mode,
bool editable)
{
bool is_mpeg1;
unsigned long val;
is_mpeg1 = (frequency < 3)?true:false;
rec_version_index = is_mpeg1?3:2;
rec_frequency_index = frequency % 3;
val = (quality << 17) |
(rec_frequency_index << 10) |
((is_mpeg1?1:0) << 9) |
(1 << 8) | /* CRC on */
(((channel_mode * 2 + 1) & 3) << 6) |
(1 << 5) /* MS-stereo */ |
(1 << 2) /* Is an original */;
mas_writemem(MAS_BANK_D0, 0x7f0, &val,1);
DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f0, %x)\n", val);
val = editable?4:0;
mas_writemem(MAS_BANK_D0, 0x7f9, &val,1);
DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f9, %x)\n", val);
val = ((!is_recording << 10) | /* Monitoring */
((source < 2)?1:2) << 8) | /* Input select */
(1 << 5) | /* SDO strobe invert */
((is_mpeg1?0:1) << 3) |
(1 << 2) | /* Inverted SIBC clock signal */
1; /* Validate */
mas_writemem(MAS_BANK_D0, 0x7f1, &val,1);
DEBUGF("mas_writemem(MAS_BANK_D0, 0x7f1, %x)\n", val);
drain_dma_buffer();
if(source == 0) /* Mic */
{
/* Copy left channel to right (mono mode) */
mas_codec_writereg(8, 0x8000);
}
else
{
/* Stereo input mode */
mas_codec_writereg(8, 0);
}
}
/* If use_mic is true, the left gain is used */
void mpeg_set_recording_gain(int left, int right, bool use_mic)
{
/* Enable both left and right A/D */
mas_codec_writereg(0x0,
(left << 12) |
(right << 8) |
(left << 4) |
(use_mic?0x0008:0) | /* Connect left A/D to mic */
0x0007);
}
#endif
#ifdef SIMULATOR #ifdef SIMULATOR
static char mpeg_stack[DEFAULT_STACK_SIZE]; static char mpeg_stack[DEFAULT_STACK_SIZE];
static char mpeg_thread_name[] = "mpeg"; static char mpeg_thread_name[] = "mpeg";