1
0
Fork 0
forked from len0rd/rockbox

Major recording rework: (1) Slight optimisation of the recording transfer. (2) Rework of the recording event loop: (a) When starting a recording, wait a bit longer before grabbing a header, increasing the chance that we get a valid one. (b) Bugfix: Always flush the whole buffer when it gets above the watermark. (c) Save in chunks for lower latency (1MB on 8MB-modded boxes, and 256KB on Ondio). (d) Hierarchical scheme of reasons to save data: stop_recording beats new_file, and new_file beats buffer_full. (e) Saving is done in one location. Decreased code size.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6560 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Jens Arnold 2005-06-04 11:29:39 +00:00
parent d9b66127ac
commit e03f40284e

View file

@ -340,12 +340,17 @@ static long lowest_watermark_level; /* Debug value to observe the buffer
usage */
#if CONFIG_HWCODEC == MAS3587F
static bool is_recording; /* We are recording */
static bool stop_pending;
unsigned long record_start_time; /* Value of current_tick when recording
was started */
unsigned long pause_start_time; /* Value of current_tick when pause was
started */
static bool saving; /* We are saving the buffer to disk */
static enum {
NOT_SAVING = 0, /* reasons to save data, sorted by importance */
BUFFER_FULL,
NEW_FILE,
STOP_RECORDING
} saving_status;
static char recording_filename[MAX_PATH]; /* argument to thread */
static char delayed_filename[MAX_PATH]; /* internal copy of above */
static int rec_frequency_index; /* For create_xing_header() calls */
@ -551,16 +556,18 @@ void rec_tick(void)
timing_info[timing_info_index++] = current_tick;
TCNT2 = 0;
#endif /* #ifdef DEBUG */
/* Note: Although this loop is run in interrupt context, further
* optimisation will do no good. The MAS would then deliver bad
* frames occasionally, as observed in extended experiments. */
i = 0;
while (PBDRH & 0x40) /* We try to read as long as EOD is high */
{
xor_b(0x08, &PADRH); /* Set PR active, independent of polarity */
delay = 0;
delay = 100;
while (PBDRH & 0x80) /* Wait until /RTW becomes active */
{
delay++;
if (delay > 100) /* Bail out if we have to wait too long */
if (--delay <= 0) /* Bail out if we have to wait too long */
{ /* i.e. the MAS doesn't want to talk to us */
xor_b(0x08, &PADRH); /* Set PR inactive */
goto transfer_end; /* and get out of here */
@ -586,7 +593,7 @@ void rec_tick(void)
#endif /* #ifdef DEBUG */
num_rec_bytes += i;
if(is_prerecording)
{
if(TIME_AFTER(current_tick, prerecord_timeout))
@ -613,9 +620,10 @@ void rec_tick(void)
if(num_bytes < 0)
num_bytes += audiobuflen;
if(audiobuflen - num_bytes < MPEG_RECORDING_LOW_WATER && !saving)
if (audiobuflen - num_bytes < MPEG_RECORDING_LOW_WATER
&& saving_status == NOT_SAVING)
{
saving = true;
saving_status = BUFFER_FULL;
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
wake_up_thread();
}
@ -998,15 +1006,13 @@ static void mpeg_thread(void)
int start_offset;
#if CONFIG_HWCODEC == MAS3587F
int amount_to_save;
int writelen;
int framelen;
unsigned long saved_header = 0;
int startpos;
int save_endpos = 0;
int rc;
long offset;
int countdown;
#endif /* #if CONFIG_HWCODEC == MAS3587F */
is_playing = false;
play_pending = false;
playing = false;
@ -1423,7 +1429,7 @@ static void mpeg_thread(void)
/* Don't read more than until the end of the buffer */
amount_to_read = MIN(audiobuflen - audiobuf_write,
amount_to_read);
#if MEM == 8
#if MEM == 8
amount_to_read = MIN(0x100000, amount_to_read);
#endif /* #if MEM == 8 */
#ifdef HAVE_MMC /* MMC is slow, so don't read too large chunks */
@ -1640,7 +1646,7 @@ static void mpeg_thread(void)
/* Wait until at least one frame is encoded and get the
frame header, for later use by the Xing header
generation */
sleep(HZ/10);
sleep(HZ/5);
saved_header = mpeg_get_last_header();
/* delayed until buffer is saved, don't open yet */
@ -1648,39 +1654,40 @@ static void mpeg_thread(void)
mpeg_file = -1;
break;
case MPEG_STOP:
DEBUGF("MPEG_STOP\n");
stop_recording();
/* Save the remaining data in the buffer */
stop_pending = true;
save_endpos = audiobuf_write;
saving_status = STOP_RECORDING;
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
break;
case MPEG_STOP_DONE:
DEBUGF("MPEG_STOP_DONE\n");
if(mpeg_file >= 0)
if (mpeg_file >= 0)
close(mpeg_file);
if(!disable_xing_header && num_rec_bytes > 0)
if (!disable_xing_header && num_rec_bytes > 0)
{
/* Create the Xing header */
mpeg_file = open(recording_filename, O_RDWR);
if(mpeg_file < 0)
if (mpeg_file < 0)
panicf("rec upd: %d (%s)", mpeg_file,
recording_filename);
/* If the number of recorded frames have
reached 0x7ffff, we can no longer trust it */
if(num_recorded_frames == 0x7ffff)
if (num_recorded_frames == 0x7ffff)
num_recorded_frames = 0;
/* Also, if we have been prerecording, the frame count
will be wrong */
if(prerecording)
if (prerecording)
num_recorded_frames = 0;
/* saved_header is saved right before stopping
@ -1697,7 +1704,7 @@ static void mpeg_thread(void)
close(mpeg_file);
}
mpeg_file = -1;
#ifdef DEBUG1
{
int i;
@ -1712,7 +1719,7 @@ static void mpeg_thread(void)
}
#endif /* #ifdef DEBUG1 */
if(prerecording)
if (prerecording)
{
start_prerecording();
}
@ -1720,202 +1727,134 @@ static void mpeg_thread(void)
break;
case MPEG_NEW_FILE:
/* Bail out when a more important save is happening */
if (saving_status > NEW_FILE)
break;
/* Make sure we have at least one complete frame
in the buffer. If we haven't recorded a single
frame within 200ms, the MAS is probably not recording
anything, and we bail out. */
countdown = 20;
amount_to_save = get_unsaved_space();
while(countdown-- && amount_to_save < 1800)
if (amount_to_save < 1800)
{
sleep(HZ/10);
sleep(HZ/5);
amount_to_save = get_unsaved_space();
}
if(amount_to_save >= 1800)
if (amount_to_save >= 1800)
{
/* Now find a frame boundary to split at */
startpos = audiobuf_write - 1800;
if(startpos < 0)
startpos += audiobuflen;
save_endpos = audiobuf_write - 1800;
if (save_endpos < 0)
save_endpos += audiobuflen;
rc = mem_find_next_frame(startpos, &offset, 1800,
rc = mem_find_next_frame(save_endpos, &offset, 1800,
saved_header);
if(rc) /* Header found? */
if (rc) /* Header found? */
{
/* offset will now contain the number of bytes to
add to startpos to find the frame boundary */
startpos += offset;
if(startpos >= audiobuflen)
startpos -= audiobuflen;
save_endpos += offset;
if (save_endpos >= audiobuflen)
save_endpos -= audiobuflen;
}
else
{
/* No header found. Let's save the whole buffer. */
startpos = audiobuf_write;
save_endpos = audiobuf_write;
}
}
else
{
/* Too few bytes recorded, timeout */
startpos = audiobuf_write;
save_endpos = audiobuf_write;
}
amount_to_save = startpos - audiobuf_read;
if(amount_to_save < 0)
amount_to_save += audiobuflen;
/* First save up to the end of the buffer */
writelen = MIN(amount_to_save,
audiobuflen - audiobuf_read);
if (mpeg_file < 0) /* delayed file opening */
saving_status = NEW_FILE;
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
case MPEG_SAVE_DATA:
if (saving_status == BUFFER_FULL)
save_endpos = audiobuf_write;
if (mpeg_file < 0) /* delayed file open */
{
mpeg_file = open(delayed_filename, O_WRONLY|O_CREAT);
if(mpeg_file < 0)
if (mpeg_file < 0)
panicf("recfile: %d", mpeg_file);
}
if(writelen)
{
rc = write(mpeg_file, audiobuf + audiobuf_read, writelen);
if(rc < 0)
{
if(errno == ENOSPC)
{
mpeg_errno = AUDIOERR_DISK_FULL;
demand_irq_enable(false);
stop_recording();
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
break;
}
else
{
panicf("spt wrt: %d", rc);
}
}
}
/* Then save the rest */
writelen = amount_to_save - writelen;
if(writelen)
{
rc = write(mpeg_file, audiobuf, writelen);
if(rc < 0)
{
if(errno == ENOSPC)
{
mpeg_errno = AUDIOERR_DISK_FULL;
demand_irq_enable(false);
stop_recording();
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
break;
}
else
{
panicf("spt wrt: %d", rc);
}
}
}
/* Advance the buffer pointers */
audiobuf_read += amount_to_save;
if(audiobuf_read >= audiobuflen)
audiobuf_read -= audiobuflen;
/* Close the current file */
rc = close(mpeg_file);
if(rc < 0)
panicf("spt cls: %d", rc);
/* Open the new file */
mpeg_file = open(recording_filename, O_WRONLY|O_CREAT);
if(mpeg_file < 0)
panicf("sptfile: %d", mpeg_file);
break;
case MPEG_SAVE_DATA:
amount_to_save = get_unsaved_space();
/* If the result is negative, the write index has
wrapped */
if(amount_to_save < 0)
{
amount_to_save = save_endpos - audiobuf_read;
if (amount_to_save < 0)
amount_to_save += audiobuflen;
}
DEBUGF("r: %x w: %x\n", audiobuf_read, audiobuf_write);
DEBUGF("ats: %x\n", amount_to_save);
/* Save data only if the buffer is getting full,
or if we should stop recording */
if(amount_to_save)
amount_to_save = MIN(amount_to_save,
audiobuflen - audiobuf_read);
#if MEM == 8
amount_to_save = MIN(0x100000, amount_to_save);
#endif
#ifdef HAVE_MMC /* MMC is slow, so don't save too large chunks at once */
amount_to_save = MIN(0x40000, amount_to_save);
#endif
rc = write(mpeg_file, audiobuf + audiobuf_read,
amount_to_save);
if (rc < 0)
{
if(audiobuflen -
amount_to_save < MPEG_RECORDING_LOW_WATER ||
stop_pending)
if (errno == ENOSPC)
{
/* Only save up to the end of the buffer */
writelen = MIN(amount_to_save,
audiobuflen - audiobuf_read);
DEBUGF("wrl: %x\n", writelen);
if (mpeg_file < 0) /* delayed file opening */
{
mpeg_file = open(delayed_filename,
O_WRONLY|O_CREAT);
if(mpeg_file < 0)
panicf("recfile: %d", mpeg_file);
}
rc = write(mpeg_file, audiobuf + audiobuf_read,
writelen);
if(rc < 0)
{
if(errno == ENOSPC)
{
mpeg_errno = AUDIOERR_DISK_FULL;
stop_recording();
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
break;
}
else
{
panicf("rec wrt: %d", rc);
}
}
audiobuf_read += amount_to_save;
if(audiobuf_read >= audiobuflen)
audiobuf_read = 0;
rc = fsync(mpeg_file);
if(rc < 0)
panicf("rec fls: %d", rc);
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
mpeg_errno = AUDIOERR_DISK_FULL;
stop_recording();
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
/* will close the file */
break;
}
else
{
saving = false;
ata_sleep();
}
panicf("rec wrt: %d", rc);
}
else
audiobuf_read += amount_to_save;
if (audiobuf_read >= audiobuflen)
audiobuf_read = 0;
if (audiobuf_read == save_endpos) /* all saved */
{
/* We have saved all data,
time to stop for real */
if(stop_pending)
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
saving = false;
ata_sleep();
switch (saving_status)
{
case BUFFER_FULL:
rc = fsync(mpeg_file);
if (rc < 0)
panicf("rec fls: %d", rc);
ata_sleep();
break;
case NEW_FILE:
/* Close the current file */
rc = close(mpeg_file);
if (rc < 0)
panicf("spt cls: %d", rc);
ata_sleep();
mpeg_file = -1;
/* copy new filename */
strcpy(delayed_filename, recording_filename);
break;
case STOP_RECORDING:
queue_post(&mpeg_queue, MPEG_STOP_DONE, NULL);
/* will close the file */
break;
default:
break;
}
saving_status = NOT_SAVING;
}
else /* tell ourselves to save the next chunk */
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
break;
case MPEG_INIT_PLAYBACK:
/* Stop the prerecording */
stop_recording();
@ -1924,12 +1863,12 @@ static void mpeg_thread(void)
break;
case MPEG_PAUSE_RECORDING:
pause_recording();
break;
pause_recording();
break;
case MPEG_RESUME_RECORDING:
resume_recording();
break;
resume_recording();
break;
case SYS_USB_CONNECTED:
/* We can safely go to USB mode if no recording
@ -2200,8 +2139,7 @@ static void start_prerecording(void)
} while(val & 1);
is_recording = true;
stop_pending = false;
saving = false;
saving_status = NOT_SAVING;
demand_irq_enable(true);
}
@ -2235,8 +2173,7 @@ static void start_recording(void)
}
is_recording = true;
stop_pending = false;
saving = false;
saving_status = NOT_SAVING;
paused = false;
/* Store the current time */