mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-11 17:16:38 +01:00
diskstream fixups for destructive track captures. crossfade fixes for destructive sources.
git-svn-id: svn://localhost/trunk/ardour2@336 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
9a92c195e3
commit
5c47492d88
4 changed files with 175 additions and 22 deletions
|
|
@ -268,6 +268,18 @@ class DiskStream : public Stateful, public sigc::trackable
|
||||||
|
|
||||||
~DiskStream();
|
~DiskStream();
|
||||||
|
|
||||||
|
enum TransitionType {
|
||||||
|
CaptureStart = 0,
|
||||||
|
CaptureEnd
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CaptureTransition {
|
||||||
|
|
||||||
|
TransitionType type;
|
||||||
|
// the start or end file frame pos
|
||||||
|
jack_nframes_t capture_val;
|
||||||
|
};
|
||||||
|
|
||||||
struct ChannelInfo {
|
struct ChannelInfo {
|
||||||
|
|
||||||
Sample *playback_wrap_buffer;
|
Sample *playback_wrap_buffer;
|
||||||
|
|
@ -292,10 +304,15 @@ class DiskStream : public Stateful, public sigc::trackable
|
||||||
|
|
||||||
RingBufferNPT<Sample>::rw_vector playback_vector;
|
RingBufferNPT<Sample>::rw_vector playback_vector;
|
||||||
RingBufferNPT<Sample>::rw_vector capture_vector;
|
RingBufferNPT<Sample>::rw_vector capture_vector;
|
||||||
|
|
||||||
|
RingBufferNPT<CaptureTransition> * capture_transition_buf;
|
||||||
|
// the following are used in the butler thread only
|
||||||
|
jack_nframes_t curr_capture_cnt;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef vector<ChannelInfo> ChannelList;
|
typedef vector<ChannelInfo> ChannelList;
|
||||||
|
|
||||||
|
|
||||||
string _name;
|
string _name;
|
||||||
ARDOUR::Session& _session;
|
ARDOUR::Session& _session;
|
||||||
ARDOUR::IO* _io;
|
ARDOUR::IO* _io;
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ DestructiveFileSource::DestructiveFileSource (string path, jack_nframes_t rate,
|
||||||
|
|
||||||
_capture_start = false;
|
_capture_start = false;
|
||||||
_capture_end = false;
|
_capture_end = false;
|
||||||
|
file_pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DestructiveFileSource::DestructiveFileSource (const XMLNode& node, jack_nframes_t rate)
|
DestructiveFileSource::DestructiveFileSource (const XMLNode& node, jack_nframes_t rate)
|
||||||
|
|
@ -90,6 +91,7 @@ DestructiveFileSource::DestructiveFileSource (const XMLNode& node, jack_nframes_
|
||||||
|
|
||||||
_capture_start = false;
|
_capture_start = false;
|
||||||
_capture_end = false;
|
_capture_end = false;
|
||||||
|
file_pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DestructiveFileSource::~DestructiveFileSource()
|
DestructiveFileSource::~DestructiveFileSource()
|
||||||
|
|
@ -160,10 +162,8 @@ DestructiveFileSource::crossfade (Sample* data, jack_nframes_t cnt, int fade_in,
|
||||||
if ((retval = file_read (xfade_buf, fade_position, xfade, workbuf)) != (ssize_t) xfade) {
|
if ((retval = file_read (xfade_buf, fade_position, xfade, workbuf)) != (ssize_t) xfade) {
|
||||||
if (retval >= 0 && errno == EAGAIN) {
|
if (retval >= 0 && errno == EAGAIN) {
|
||||||
/* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
|
/* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
|
||||||
/* short or no data there */
|
* short or no data there */
|
||||||
|
memset (xfade_buf, 0, xfade * sizeof(Sample));
|
||||||
xfade = retval;
|
|
||||||
nofade = cnt - xfade;
|
|
||||||
} else {
|
} else {
|
||||||
error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
|
error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -244,12 +244,40 @@ DestructiveFileSource::write (Sample* data, jack_nframes_t cnt, char * workbuf)
|
||||||
|
|
||||||
jack_nframes_t oldlen;
|
jack_nframes_t oldlen;
|
||||||
|
|
||||||
if (_capture_start) {
|
if (_capture_start && _capture_end) {
|
||||||
|
_capture_start = false;
|
||||||
|
_capture_end = false;
|
||||||
|
|
||||||
|
/* move to the correct location place */
|
||||||
|
file_pos = capture_start_frame;
|
||||||
|
|
||||||
|
cerr << "First frame of capture will be at " << file_pos << " and last at: " << file_pos + cnt << endl;
|
||||||
|
|
||||||
|
// split cnt in half
|
||||||
|
jack_nframes_t subcnt = cnt / 2;
|
||||||
|
jack_nframes_t ofilepos = file_pos;
|
||||||
|
|
||||||
|
// fade in
|
||||||
|
if (crossfade (data, subcnt, 1, workbuf) != subcnt) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_pos += subcnt;
|
||||||
|
Sample * tmpdata = data + subcnt;
|
||||||
|
|
||||||
|
// fade out
|
||||||
|
subcnt = cnt - subcnt;
|
||||||
|
if (crossfade (tmpdata, subcnt, 0, workbuf) != subcnt) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_pos = ofilepos; // adjusted below
|
||||||
|
}
|
||||||
|
else if (_capture_start) {
|
||||||
_capture_start = false;
|
_capture_start = false;
|
||||||
_capture_end = false;
|
_capture_end = false;
|
||||||
|
|
||||||
/* move to the correct location place */
|
/* move to the correct location place */
|
||||||
//file_pos = data_offset + (capture_start_frame * sizeof (Sample));
|
|
||||||
file_pos = capture_start_frame;
|
file_pos = capture_start_frame;
|
||||||
|
|
||||||
cerr << "First frame of capture will be at " << file_pos << endl;
|
cerr << "First frame of capture will be at " << file_pos << endl;
|
||||||
|
|
|
||||||
|
|
@ -104,9 +104,12 @@ DiskStream::init_channel (ChannelInfo &chan)
|
||||||
chan.source = 0;
|
chan.source = 0;
|
||||||
chan.current_capture_buffer = 0;
|
chan.current_capture_buffer = 0;
|
||||||
chan.current_playback_buffer = 0;
|
chan.current_playback_buffer = 0;
|
||||||
|
chan.curr_capture_cnt = 0;
|
||||||
|
|
||||||
chan.playback_buf = new RingBufferNPT<Sample> (_session.diskstream_buffer_size());
|
chan.playback_buf = new RingBufferNPT<Sample> (_session.diskstream_buffer_size());
|
||||||
chan.capture_buf = new RingBufferNPT<Sample> (_session.diskstream_buffer_size());
|
chan.capture_buf = new RingBufferNPT<Sample> (_session.diskstream_buffer_size());
|
||||||
|
chan.capture_transition_buf = new RingBufferNPT<CaptureTransition> (128);
|
||||||
|
|
||||||
|
|
||||||
/* touch the ringbuffer buffers, which will cause
|
/* touch the ringbuffer buffers, which will cause
|
||||||
them to be mapped into locked physical RAM if
|
them to be mapped into locked physical RAM if
|
||||||
|
|
@ -115,6 +118,7 @@ DiskStream::init_channel (ChannelInfo &chan)
|
||||||
*/
|
*/
|
||||||
memset (chan.playback_buf->buffer(), 0, sizeof (Sample) * chan.playback_buf->bufsize());
|
memset (chan.playback_buf->buffer(), 0, sizeof (Sample) * chan.playback_buf->bufsize());
|
||||||
memset (chan.capture_buf->buffer(), 0, sizeof (Sample) * chan.capture_buf->bufsize());
|
memset (chan.capture_buf->buffer(), 0, sizeof (Sample) * chan.capture_buf->bufsize());
|
||||||
|
memset (chan.capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * chan.capture_transition_buf->bufsize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -196,6 +200,7 @@ DiskStream::destroy_channel (ChannelInfo &chan)
|
||||||
|
|
||||||
delete chan.playback_buf;
|
delete chan.playback_buf;
|
||||||
delete chan.capture_buf;
|
delete chan.capture_buf;
|
||||||
|
delete chan.capture_transition_buf;
|
||||||
|
|
||||||
chan.playback_buf = 0;
|
chan.playback_buf = 0;
|
||||||
chan.capture_buf = 0;
|
chan.capture_buf = 0;
|
||||||
|
|
@ -615,9 +620,22 @@ DiskStream::check_record_status (jack_nframes_t transport_frame, jack_nframes_t
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_flags & Recordable) {
|
if (_flags & Recordable) {
|
||||||
cerr << "START RECORD @ " << capture_start_frame << " = " << capture_start_frame * sizeof (Sample) << endl;
|
cerr << "START RECORD @ " << capture_start_frame << endl;
|
||||||
|
|
||||||
for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
|
for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
|
||||||
(*chan).write_source->mark_capture_start (capture_start_frame);
|
|
||||||
|
RingBufferNPT<CaptureTransition>::rw_vector transvec;
|
||||||
|
(*chan).capture_transition_buf->get_write_vector(&transvec);
|
||||||
|
|
||||||
|
if (transvec.len[0] > 0) {
|
||||||
|
transvec.buf[0]->type = CaptureStart;
|
||||||
|
transvec.buf[0]->capture_val = capture_start_frame;
|
||||||
|
(*chan).capture_transition_buf->increment_write_ptr(1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// bad!
|
||||||
|
cerr << "capture_transition_buf is full on rec start! inconceivable!" << endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1442,6 +1460,7 @@ DiskStream::do_flush (char * workbuf, bool force_flush)
|
||||||
uint32_t to_write;
|
uint32_t to_write;
|
||||||
int32_t ret = 0;
|
int32_t ret = 0;
|
||||||
RingBufferNPT<Sample>::rw_vector vector;
|
RingBufferNPT<Sample>::rw_vector vector;
|
||||||
|
RingBufferNPT<CaptureTransition>::rw_vector transvec;
|
||||||
jack_nframes_t total;
|
jack_nframes_t total;
|
||||||
|
|
||||||
/* important note: this function will write *AT MOST*
|
/* important note: this function will write *AT MOST*
|
||||||
|
|
@ -1462,10 +1481,12 @@ DiskStream::do_flush (char * workbuf, bool force_flush)
|
||||||
|
|
||||||
total = vector.len[0] + vector.len[1];
|
total = vector.len[0] + vector.len[1];
|
||||||
|
|
||||||
|
|
||||||
if (total == 0 || (total < disk_io_chunk_frames && !force_flush && was_recording)) {
|
if (total == 0 || (total < disk_io_chunk_frames && !force_flush && was_recording)) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* if there are 2+ chunks of disk i/o possible for
|
/* if there are 2+ chunks of disk i/o possible for
|
||||||
this track, let the caller know so that it can arrange
|
this track, let the caller know so that it can arrange
|
||||||
for us to be called again, ASAP.
|
for us to be called again, ASAP.
|
||||||
|
|
@ -1483,14 +1504,85 @@ DiskStream::do_flush (char * workbuf, bool force_flush)
|
||||||
|
|
||||||
to_write = min (disk_io_chunk_frames, (jack_nframes_t) vector.len[0]);
|
to_write = min (disk_io_chunk_frames, (jack_nframes_t) vector.len[0]);
|
||||||
|
|
||||||
|
|
||||||
|
// check the transition buffer when recording destructive
|
||||||
|
// important that we get this after the capture buf
|
||||||
|
|
||||||
|
if (destructive()) {
|
||||||
|
(*chan).capture_transition_buf->get_read_vector(&transvec);
|
||||||
|
size_t transcount = transvec.len[0] + transvec.len[1];
|
||||||
|
bool have_start = false;
|
||||||
|
size_t ti;
|
||||||
|
|
||||||
|
for (ti=0; ti < transcount; ++ti) {
|
||||||
|
CaptureTransition & captrans = (ti < transvec.len[0]) ? transvec.buf[0][ti] : transvec.buf[1][ti-transvec.len[0]];
|
||||||
|
|
||||||
|
if (captrans.type == CaptureStart) {
|
||||||
|
// by definition, the first data we got above represents the given capture pos
|
||||||
|
cerr << "DS " << name() << " got CaptureStart at " << captrans.capture_val << endl;
|
||||||
|
|
||||||
|
(*chan).write_source->mark_capture_start (captrans.capture_val);
|
||||||
|
|
||||||
|
(*chan).curr_capture_cnt = 0;
|
||||||
|
|
||||||
|
have_start = true;
|
||||||
|
}
|
||||||
|
else if (captrans.type == CaptureEnd) {
|
||||||
|
|
||||||
|
// capture end, the capture_val represents total frames in capture
|
||||||
|
|
||||||
|
if (captrans.capture_val <= (*chan).curr_capture_cnt + to_write) {
|
||||||
|
|
||||||
|
cerr << "DS " << name() << " got CaptureEnd with " << captrans.capture_val << endl;
|
||||||
|
// shorten to make the write a perfect fit
|
||||||
|
uint32_t nto_write = (captrans.capture_val - (*chan).curr_capture_cnt);
|
||||||
|
if (have_start) {
|
||||||
|
// starts and ends within same chunk we're processing
|
||||||
|
cerr << "Starts and ends within same chunk: adjusting to_write from: "
|
||||||
|
<< to_write << " to: " << nto_write << endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cerr << "Ends within chunk: adjusting to_write to: "
|
||||||
|
<< to_write << " to: " << nto_write << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nto_write < to_write) {
|
||||||
|
ret = 1; // should we?
|
||||||
|
}
|
||||||
|
to_write = nto_write;
|
||||||
|
|
||||||
|
(*chan).write_source->mark_capture_end ();
|
||||||
|
|
||||||
|
// increment past this transition, but go no further
|
||||||
|
++ti;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// actually ends just beyond this chunk, so force more work
|
||||||
|
cerr << "DS " << name() << " got CaptureEnd beyond our chunk, cnt of: "
|
||||||
|
<< captrans.capture_val << " leaving on queue" << endl;
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ti > 0) {
|
||||||
|
(*chan).capture_transition_buf->increment_read_ptr(ti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ((!(*chan).write_source) || (*chan).write_source->write (vector.buf[0], to_write, workbuf) != to_write) {
|
if ((!(*chan).write_source) || (*chan).write_source->write (vector.buf[0], to_write, workbuf) != to_write) {
|
||||||
error << string_compose(_("DiskStream %1: cannot write to disk"), _id) << endmsg;
|
error << string_compose(_("DiskStream %1: cannot write to disk"), _id) << endmsg;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*chan).capture_buf->increment_read_ptr (to_write);
|
(*chan).capture_buf->increment_read_ptr (to_write);
|
||||||
|
(*chan).curr_capture_cnt += to_write;
|
||||||
|
|
||||||
if ((to_write == vector.len[0]) && (total > to_write) && (to_write < disk_io_chunk_frames)) {
|
if ((to_write == vector.len[0]) && (total > to_write) && (to_write < disk_io_chunk_frames) && !destructive()) {
|
||||||
|
|
||||||
/* we wrote all of vector.len[0] but it wasn't an entire
|
/* we wrote all of vector.len[0] but it wasn't an entire
|
||||||
disk_io_chunk_frames of data, so arrange for some part
|
disk_io_chunk_frames of data, so arrange for some part
|
||||||
|
|
@ -1507,6 +1599,7 @@ DiskStream::do_flush (char * workbuf, bool force_flush)
|
||||||
_write_data_count += (*chan).write_source->write_data_count();
|
_write_data_count += (*chan).write_source->write_data_count();
|
||||||
|
|
||||||
(*chan).capture_buf->increment_read_ptr (to_write);
|
(*chan).capture_buf->increment_read_ptr (to_write);
|
||||||
|
(*chan).curr_capture_cnt += to_write;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1700,16 +1793,31 @@ DiskStream::finish_capture (bool rec_monitors_input)
|
||||||
{
|
{
|
||||||
was_recording = false;
|
was_recording = false;
|
||||||
|
|
||||||
if (_flags & Recordable) {
|
|
||||||
for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
|
|
||||||
(*chan).write_source->mark_capture_end ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (capture_captured == 0) {
|
if (capture_captured == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((_flags & Recordable) && destructive()) {
|
||||||
|
cerr << "RECORD END @ " << capture_start_frame + capture_captured << endl;
|
||||||
|
for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
|
||||||
|
|
||||||
|
RingBufferNPT<CaptureTransition>::rw_vector transvec;
|
||||||
|
(*chan).capture_transition_buf->get_write_vector(&transvec);
|
||||||
|
|
||||||
|
|
||||||
|
if (transvec.len[0] > 0) {
|
||||||
|
transvec.buf[0]->type = CaptureEnd;
|
||||||
|
transvec.buf[0]->capture_val = capture_captured;
|
||||||
|
(*chan).capture_transition_buf->increment_write_ptr(1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// bad!
|
||||||
|
cerr << "capture_transition_buf is full when stopping record! inconceivable!" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
CaptureInfo* ci = new CaptureInfo;
|
CaptureInfo* ci = new CaptureInfo;
|
||||||
|
|
||||||
ci->start = capture_start_frame;
|
ci->start = capture_start_frame;
|
||||||
|
|
|
||||||
|
|
@ -1125,7 +1125,7 @@ FileSource::read_pcm_24 (Sample *dst, jack_nframes_t start, jack_nframes_t cnt,
|
||||||
|
|
||||||
if ((nread = pread (fd, (char *) workbuf, byte_cnt, data_offset + (start * _sample_size))) != byte_cnt) {
|
if ((nread = pread (fd, (char *) workbuf, byte_cnt, data_offset + (start * _sample_size))) != byte_cnt) {
|
||||||
|
|
||||||
cerr << "FileSource: \""
|
cerr << "May be OK - FileSource: \""
|
||||||
<< _path
|
<< _path
|
||||||
<< "\" bad 24bit read at frame "
|
<< "\" bad 24bit read at frame "
|
||||||
<< start
|
<< start
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue