X-Git-Url: https://main.carlh.net/gitweb/?p=ardour.git;a=blobdiff_plain;f=libs%2Fardour%2Faudio_diskstream.cc;h=330b9d582acda0367d509993f54c0b1b2b98e339;hp=e0086fa2950d5b0919b0d39741999d1444584927;hb=c8c6bca6587450ff64303dbc994a4cd28d6ce7aa;hpb=b37e3229f79afdda9a37021f5dd8d4655b754be6 diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index e0086fa295..330b9d582a 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -16,7 +16,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include #include #include @@ -28,6 +27,7 @@ #include #include +#include "pbd/gstdio_compat.h" #include "pbd/error.h" #include "pbd/xml++.h" #include "pbd/memento_command.h" @@ -46,22 +46,23 @@ #include "ardour/debug.h" #include "ardour/io.h" #include "ardour/playlist_factory.h" +#include "ardour/profile.h" #include "ardour/region_factory.h" #include "ardour/session.h" #include "ardour/session_playlists.h" +#include "ardour/sndfile_helpers.h" #include "ardour/source_factory.h" #include "ardour/track.h" #include "ardour/types.h" #include "ardour/utils.h" -#include "i18n.h" +#include "pbd/i18n.h" #include using namespace std; using namespace ARDOUR; using namespace PBD; -size_t AudioDiskstream::_working_buffers_size = 0; Sample* AudioDiskstream::_mixdown_buffer = 0; gain_t* AudioDiskstream::_gain_buffer = 0; @@ -74,6 +75,10 @@ AudioDiskstream::AudioDiskstream (Session &sess, const string &name, Diskstream: in_set_state = true; use_new_playlist (); in_set_state = false; + + if (flag & Destructive) { + use_destructive_playlist (); + } } AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node) @@ -128,11 +133,13 @@ AudioDiskstream::~AudioDiskstream () void AudioDiskstream::allocate_working_buffers() { - assert(disk_io_frames() > 0); - - _working_buffers_size = disk_io_frames(); - _mixdown_buffer = new Sample[_working_buffers_size]; - _gain_buffer = new gain_t[_working_buffers_size]; + /* with varifill buffer refilling, we compute the read size in bytes (to optimize + for disk i/o bandwidth) and then convert back into samples. These buffers + need to reflect the maximum size we could use, which is 4MB reads, or 2M samples + using 16 bit samples. + */ + _mixdown_buffer = new Sample[2*1048576]; + _gain_buffer = new gain_t[2*1048576]; } void @@ -140,7 +147,6 @@ AudioDiskstream::free_working_buffers() { delete [] _mixdown_buffer; delete [] _gain_buffer; - _working_buffers_size = 0; _mixdown_buffer = 0; _gain_buffer = 0; } @@ -148,7 +154,7 @@ AudioDiskstream::free_working_buffers() void AudioDiskstream::non_realtime_input_change () { - bool need_new_write_sources = false; + bool need_write_sources = false; { Glib::Threads::Mutex::Lock lm (state_lock); @@ -157,7 +163,12 @@ AudioDiskstream::non_realtime_input_change () return; } - if (input_change_pending.type == IOChange::ConfigurationChanged) { + boost::shared_ptr cr = channels.reader(); + if (!cr->empty() && !cr->front()->write_source) { + need_write_sources = true; + } + + if (input_change_pending.type & IOChange::ConfigurationChanged) { RCUWriter writer (channels); boost::shared_ptr c = writer.get_copy(); @@ -169,7 +180,7 @@ AudioDiskstream::non_realtime_input_change () remove_channel_from (c, _n_channels.n_audio() - _io->n_ports().n_audio()); } - need_new_write_sources = true; + need_write_sources = true; } if (input_change_pending.type & IOChange::ConnectionsChanged) { @@ -183,7 +194,7 @@ AudioDiskstream::non_realtime_input_change () /* implicit unlock */ } - if (need_new_write_sources) { + if (need_write_sources) { reset_write_sources (false); } @@ -202,9 +213,9 @@ AudioDiskstream::non_realtime_locate (framepos_t location) /* now refill channel buffers */ if (speed() != 1.0f || speed() != -1.0f) { - seek ((framepos_t) (location * (double) speed())); + seek ((framepos_t) (location * (double) speed()), true); } else { - seek (location); + seek (location, true); } } @@ -222,7 +233,7 @@ AudioDiskstream::get_input_sources () connections.clear (); - if (_io->nth (n)->get_connections (connections) == 0) { + if ((_io->nth (n).get()) && (_io->nth (n)->get_connections (connections) == 0)) { if (!(*chan)->source.name.empty()) { // _source->disable_metering (); } @@ -328,10 +339,13 @@ AudioDiskstream::setup_destructive_playlist () PropertyList plist; plist.add (Properties::name, _name.val()); plist.add (Properties::start, 0); - plist.add (Properties::length, max_framepos - (max_framepos - srcs.front()->natural_position())); + plist.add (Properties::length, max_framepos - srcs.front()->natural_position()); boost::shared_ptr region (RegionFactory::create (srcs, plist)); _playlist->add_region (region, srcs.front()->natural_position()); + + /* apply region properties and update write sources */ + use_destructive_playlist(); } void @@ -343,7 +357,14 @@ AudioDiskstream::use_destructive_playlist () with the (presumed single, full-extent) region. */ - boost::shared_ptr rp = _playlist->find_next_region (_session.current_start_frame(), Start, 1); + boost::shared_ptr rp; + { + const RegionList& rl (_playlist->region_list_property().rlist()); + if (rl.size() > 0) { + assert((rl.size() == 1)); + rp = rl.front(); + } + } if (!rp) { reset_write_sources (false, true); @@ -356,9 +377,9 @@ AudioDiskstream::use_destructive_playlist () throw failed_constructor(); } - /* be sure to stretch the region out to the maximum length */ + /* be sure to stretch the region out to the maximum length (non-musical)*/ - region->set_length (max_framepos - region->position()); + region->set_length (max_framepos - region->position(), 0); uint32_t n; ChannelList::iterator chan; @@ -455,8 +476,12 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t if (record_enabled()) { Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes); + // XXX should this be transport_frame + nframes - 1 ? coverage() expects its parameter ranges to include their end points + // XXX also, first_recordable_frame & last_recordable_frame may both be == max_framepos: coverage() will return OverlapNone in that case. Is thak OK? calculate_record_range (ot, transport_frame, nframes, rec_nframes, rec_offset); + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: this time record %2 of %3 frames, offset %4\n", _name, rec_nframes, nframes, rec_offset)); + if (rec_nframes && !was_recording) { capture_captured = 0; was_recording = true; @@ -498,12 +523,14 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t Sample *buf = bufs.get_audio (n).data(rec_offset); memcpy (chaninfo->current_capture_buffer, buf, sizeof (Sample) * rec_nframes); - + } else { framecnt_t total = chaninfo->capture_vector.len[0] + chaninfo->capture_vector.len[1]; if (rec_nframes > total) { + DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 overrun in %2, rec_nframes = %3 total space = %4\n", + DEBUG_THREAD_SELF, name(), rec_nframes, total)); DiskOverrun (); return -1; } @@ -572,14 +599,14 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t } if ((_track->monitoring_state () & MonitoringDisk) || collect_playback) { - + /* we're doing playback */ framecnt_t necessary_samples; /* no varispeed playback if we're recording, because the output .... TBD */ - if (rec_nframes == 0 && _actual_speed != 1.0f) { + if (rec_nframes == 0 && _actual_speed != 1.0) { necessary_samples = (framecnt_t) ceil ((nframes * fabs (_actual_speed))) + 2; } else { necessary_samples = nframes; @@ -610,6 +637,8 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t if (necessary_samples > total) { cerr << _name << " Need " << necessary_samples << " total = " << total << endl; cerr << "underrun for " << _name << endl; + DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, rec_nframes = %3 total space = %4\n", + DEBUG_THREAD_SELF, name(), rec_nframes, total)); DiskUnderrun (); return -1; @@ -626,7 +655,7 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t memcpy ((char *) chaninfo->playback_wrap_buffer, chaninfo->playback_vector.buf[0], chaninfo->playback_vector.len[0] * sizeof (Sample)); - + /* Copy buf[1] from playback_buf */ memcpy (chaninfo->playback_wrap_buffer + chaninfo->playback_vector.len[0], chaninfo->playback_vector.buf[1], @@ -645,13 +674,13 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t int channel = 0; for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) { ChannelInfo* chaninfo (*chan); - + playback_distance = interpolation.interpolate ( channel, nframes, chaninfo->current_playback_buffer, chaninfo->speed_buffer); - + chaninfo->current_playback_buffer = chaninfo->speed_buffer; } - + } else { playback_distance = nframes; } @@ -662,20 +691,20 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t if (need_disk_signal) { /* copy data over to buffer set */ - + size_t n_buffers = bufs.count().n_audio(); size_t n_chans = c->size(); gain_t scaling = 1.0f; - + if (n_chans > n_buffers) { scaling = ((float) n_buffers)/n_chans; } for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { - + AudioBuffer& buf (bufs.get_audio (n%n_buffers)); ChannelInfo* chaninfo (*chan); - + if (n < n_chans) { if (scaling != 1.0f) { buf.read_from_with_gain (chaninfo->current_playback_buffer, nframes, scaling); @@ -695,7 +724,7 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t ChanCount cnt (DataType::AUDIO, n_chans); cnt.set (DataType::MIDI, bufs.count().n_midi()); bufs.set_count (cnt); - + /* extra buffers will already be silent, so leave them alone */ } @@ -758,6 +787,7 @@ AudioDiskstream::commit (framecnt_t playback_distance) if (adjust_capture_position != 0) { capture_captured += adjust_capture_position; + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 now captured %2 (by %3)\n", name(), capture_captured, adjust_capture_position)); adjust_capture_position = 0; } @@ -773,10 +803,10 @@ AudioDiskstream::commit (framecnt_t playback_distance) } } else { if (_io && _io->active()) { - need_butler = ((framecnt_t) c->front()->playback_buf->write_space() >= disk_io_chunk_frames) - || ((framecnt_t) c->front()->capture_buf->read_space() >= disk_io_chunk_frames); + need_butler = ((framecnt_t) c->front()->playback_buf->write_space() >= disk_read_chunk_frames) + || ((framecnt_t) c->front()->capture_buf->read_space() >= disk_write_chunk_frames); } else { - need_butler = ((framecnt_t) c->front()->capture_buf->read_space() >= disk_io_chunk_frames); + need_butler = ((framecnt_t) c->front()->capture_buf->read_space() >= disk_write_chunk_frames); } } @@ -898,9 +928,15 @@ AudioDiskstream::seek (framepos_t frame, bool complete_refill) file_frame = frame; if (complete_refill) { - while ((ret = do_refill_with_alloc ()) > 0) ; + /* call _do_refill() to refill the entire buffer, using + the largest reads possible. + */ + while ((ret = do_refill_with_alloc (false)) > 0) ; } else { - ret = do_refill_with_alloc (); + /* call _do_refill() to refill just one chunk, and then + return. + */ + ret = do_refill_with_alloc (true); } return ret; @@ -927,7 +963,7 @@ AudioDiskstream::internal_playback_seek (framecnt_t distance) boost::shared_ptr c = channels.reader(); for (chan = c->begin(); chan != c->end(); ++chan) { - (*chan)->playback_buf->increment_read_ptr (llabs(distance)); + (*chan)->playback_buf->increment_read_ptr (::llabs(distance)); } if (first_recordable_frame < max_framepos) { @@ -984,6 +1020,7 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, if (loc && start >= loop_end) { start = loop_start + ((start - loop_start) % loop_length); } + } if (reversed) { @@ -1041,12 +1078,18 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, } int -AudioDiskstream::do_refill_with_alloc () +AudioDiskstream::_do_refill_with_alloc (bool partial_fill) { - Sample* mix_buf = new Sample[disk_io_chunk_frames]; - float* gain_buf = new float[disk_io_chunk_frames]; + /* We limit disk reads to at most 4MB chunks, which with floating point + samples would be 1M samples. But we might use 16 or 14 bit samples, + in which case 4MB is more samples than that. Therefore size this for + the smallest sample value .. 4MB = 2M samples (16 bit). + */ + + Sample* mix_buf = new Sample[2*1048576]; + float* gain_buf = new float[2*1048576]; - int ret = _do_refill(mix_buf, gain_buf); + int ret = _do_refill (mix_buf, gain_buf, (partial_fill ? disk_read_chunk_frames : 0)); delete [] mix_buf; delete [] gain_buf; @@ -1056,9 +1099,15 @@ AudioDiskstream::do_refill_with_alloc () /** Get some more data from disk and put it in our channels' playback_bufs, * if there is suitable space in them. + * + * If fill_level is non-zero, then we will refill the buffer so that there is + * still at least fill_level samples of space left to be filled. This is used + * after locates so that we do not need to wait to fill the entire buffer. + * */ + int -AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) +AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer, framecnt_t fill_level) { int32_t ret = 0; framecnt_t to_read; @@ -1071,6 +1120,14 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) boost::shared_ptr c = channels.reader(); framecnt_t ts; + /* do not read from disk while session is marked as Loading, to avoid + useless redundant I/O. + */ + + if (_session.state_of_the_state() & Session::Loading) { + return 0; + } + if (c->empty()) { return 0; } @@ -1090,27 +1147,27 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) return 0; } - /* if there are 2+ chunks of disk i/o possible for - this track, let the caller know so that it can arrange - for us to be called again, ASAP. - */ - - if (total_space >= (_slaved ? 3 : 2) * disk_io_chunk_frames) { - ret = 1; + if (fill_level) { + if (fill_level < total_space) { + total_space -= fill_level; + } else { + /* we can't do anything with it */ + fill_level = 0; + } } /* if we're running close to normal speed and there isn't enough - space to do disk_io_chunk_frames of I/O, then don't bother. + space to do disk_read_chunk_frames of I/O, then don't bother. at higher speeds, just do it because the sync between butler and audio thread may not be good enough. - Note: it is a design assumption that disk_io_chunk_frames is smaller + Note: it is a design assumption that disk_read_chunk_frames is smaller than the playback buffer size, so this check should never trip when the playback buffer is empty. */ - if ((total_space < disk_io_chunk_frames) && fabs (_actual_speed) < 2.0f) { + if ((total_space < disk_read_chunk_frames) && fabs (_actual_speed) < 2.0f) { return 0; } @@ -1123,10 +1180,6 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) return 0; } - /* never do more than disk_io_chunk_frames worth of disk input per call (limit doesn't apply for memset) */ - - total_space = min (disk_io_chunk_frames, total_space); - if (reversed) { if (file_frame == 0) { @@ -1193,6 +1246,30 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) framepos_t file_frame_tmp = 0; + /* total_space is in samples. We want to optimize read sizes in various sizes using bytes */ + + const size_t bits_per_sample = format_data_width (_session.config.get_native_file_data_format()); + size_t total_bytes = total_space * bits_per_sample / 8; + + /* chunk size range is 256kB to 4MB. Bigger is faster in terms of MB/sec, but bigger chunk size always takes longer + */ + size_t byte_size_for_read = max ((size_t) (256 * 1024), min ((size_t) (4 * 1048576), total_bytes)); + + /* find nearest (lower) multiple of 16384 */ + + byte_size_for_read = (byte_size_for_read / 16384) * 16384; + + /* now back to samples */ + + framecnt_t samples_to_read = byte_size_for_read / (bits_per_sample / 8); + + //cerr << name() << " will read " << byte_size_for_read << " out of total bytes " << total_bytes << " in buffer of " + // << c->front()->playback_buf->bufsize() * bits_per_sample / 8 << " bps = " << bits_per_sample << endl; + // cerr << name () << " read samples = " << samples_to_read << " out of total space " << total_space << " in buffer of " << c->front()->playback_buf->bufsize() << " samples\n"; + + // uint64_t before = g_get_monotonic_time (); + // uint64_t elapsed; + for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) { ChannelInfo* chan (*i); @@ -1202,14 +1279,14 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) chan->playback_buf->get_write_vector (&vector); - if ((framecnt_t) vector.len[0] > disk_io_chunk_frames) { + if ((framecnt_t) vector.len[0] > samples_to_read) { /* we're not going to fill the first chunk, so certainly do not bother with the other part. it won't be connected with the part we do fill, as in: .... => writable space ++++ => readable space - ^^^^ => 1 x disk_io_chunk_frames that would be filled + ^^^^ => 1 x disk_read_chunk_frames that would be filled |......|+++++++++++++|...............................| buf1 buf0 @@ -1234,7 +1311,7 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) len2 = vector.len[1]; to_read = min (ts, len1); - to_read = min (to_read, disk_io_chunk_frames); + to_read = min (to_read, (framecnt_t) samples_to_read); assert (to_read >= 0); @@ -1253,8 +1330,9 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) if (to_read) { - /* we read all of vector.len[0], but it wasn't an entire disk_io_chunk_frames of data, - so read some or all of vector.len[1] as well. + /* we read all of vector.len[0], but it wasn't the + entire samples_to_read of data, so read some or + all of vector.len[1] as well. */ if (read (buf2, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) { @@ -1271,22 +1349,28 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer) } + // elapsed = g_get_monotonic_time () - before; + // cerr << "\tbandwidth = " << (byte_size_for_read / 1048576.0) / (elapsed/1000000.0) << "MB/sec\n"; + file_frame = file_frame_tmp; assert (file_frame >= 0); - out: + ret = ((total_space - samples_to_read) > disk_read_chunk_frames); + c->front()->playback_buf->get_write_vector (&vector); + + out: return ret; } /** Flush pending data to disk. * - * Important note: this function will write *AT MOST* disk_io_chunk_frames + * Important note: this function will write *AT MOST* disk_write_chunk_frames * of data to disk. it will never write more than that. If it writes that * much and there is more than that waiting to be written, it will return 1, * otherwise 0 on success or -1 on failure. * - * If there is less than disk_io_chunk_frames to be written, no data will be + * If there is less than disk_write_chunk_frames to be written, no data will be * written at all unless @a force_flush is true. */ int @@ -1310,7 +1394,7 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush) total = vector.len[0] + vector.len[1]; - if (total == 0 || (total < disk_io_chunk_frames && !force_flush && was_recording)) { + if (total == 0 || (total < disk_write_chunk_frames && !force_flush && was_recording)) { goto out; } @@ -1325,11 +1409,11 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush) let the caller know too. */ - if (total >= 2 * disk_io_chunk_frames || ((force_flush || !was_recording) && total > disk_io_chunk_frames)) { + if (total >= 2 * disk_write_chunk_frames || ((force_flush || !was_recording) && total > disk_write_chunk_frames)) { ret = 1; } - to_write = min (disk_io_chunk_frames, (framecnt_t) vector.len[0]); + to_write = min (disk_write_chunk_frames, (framecnt_t) vector.len[0]); // check the transition buffer when recording destructive // important that we get this after the capture buf @@ -1389,14 +1473,16 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush) (*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) && !destructive()) { + if ((to_write == vector.len[0]) && (total > to_write) && (to_write < disk_write_chunk_frames) && !destructive()) { /* 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_write_chunk_frames of data, so arrange for some part of vector.len[1] to be flushed to disk as well. */ - to_write = min ((framecnt_t)(disk_io_chunk_frames - to_write), (framecnt_t) vector.len[1]); + to_write = min ((framecnt_t)(disk_write_chunk_frames - to_write), (framecnt_t) vector.len[1]); + + DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 additional write of %2\n", name(), to_write)); if ((*chan)->write_source->write (vector.buf[1], to_write) != to_write) { error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg; @@ -1494,6 +1580,8 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo if (Config->get_auto_analyse_audio()) { Analyser::queue_source_for_analysis (s, true); } + + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("newly captured source %1 length %2\n", s->path(), s->length (0))); } } @@ -1542,8 +1630,6 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end()); - // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n"; - _playlist->clear_changes (); _playlist->set_capture_insertion_in_progress (true); _playlist->freeze (); @@ -1554,8 +1640,8 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo RegionFactory::region_name (region_name, whole_file_region_name, false); - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n", - _name, (*ci)->start, (*ci)->frames, region_name)); + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture bufpos %5 start @ %2 length %3 add new region %4\n", + _name, (*ci)->start, (*ci)->frames, region_name, buffer_position)); try { @@ -1610,18 +1696,6 @@ AudioDiskstream::transport_looped (framepos_t transport_frame) // all we need to do is finish this capture, with modified capture length boost::shared_ptr c = channels.reader(); - // adjust the capture length knowing that the data will be recorded to disk - // only necessary after the first loop where we're recording - if (capture_info.size() == 0) { - capture_captured += _capture_offset; - - if (_alignment_style == ExistingMaterial) { - capture_captured += _session.worst_output_latency(); - } else { - capture_captured += _roll_delay; - } - } - finish_capture (c); // the next region will start recording via the normal mechanism @@ -1698,7 +1772,7 @@ AudioDiskstream::finish_capture (boost::shared_ptr c) accessors, so that invalidation will not occur (both non-realtime). */ - // cerr << "Finish capture, add new CI, " << ci->start << '+' << ci->frames << endl; + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("Finish capture, add new CI, %1 + %2\n", ci->start, ci->frames)); capture_info.push_back (ci); capture_captured = 0; @@ -1710,7 +1784,7 @@ AudioDiskstream::finish_capture (boost::shared_ptr c) void AudioDiskstream::set_record_enabled (bool yn) { - if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) { + if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0 || record_safe ()) { return; } @@ -1735,10 +1809,39 @@ AudioDiskstream::set_record_enabled (bool yn) } } +void +AudioDiskstream::set_record_safe (bool yn) +{ + if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) { + return; + } + + /* can't rec-safe in destructive mode if transport is before start ???? + REQUIRES REVIEW */ + + if (destructive() && yn && _session.transport_frame() < _session.current_start_frame()) { + return; + } + + /* yes, i know that this not proof against race conditions, but its + good enough. i think. + */ + + if (record_safe () != yn) { + if (yn) { + engage_record_safe (); + } else { + disengage_record_safe (); + } + + RecordSafeChanged (); /* EMIT SIGNAL */ + } +} + bool AudioDiskstream::prep_record_enable () { - if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) { + if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0 || record_safe ()) { // REQUIRES REVIEW "|| record_safe ()" return false; } @@ -1758,13 +1861,15 @@ AudioDiskstream::prep_record_enable () for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { (*chan)->source.request_input_monitoring (!(_session.config.get_auto_input() && rolling)); capturing_sources.push_back ((*chan)->write_source); - (*chan)->write_source->mark_streaming_write_started (); + Source::Lock lock((*chan)->write_source->mutex()); + (*chan)->write_source->mark_streaming_write_started (lock); } } else { for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { capturing_sources.push_back ((*chan)->write_source); - (*chan)->write_source->mark_streaming_write_started (); + Source::Lock lock((*chan)->write_source->mutex()); + (*chan)->write_source->mark_streaming_write_started (lock); } } @@ -1790,7 +1895,7 @@ AudioDiskstream::get_state () { XMLNode& node (Diskstream::get_state()); char buf[64] = ""; - LocaleGuard lg (X_("POSIX")); + LocaleGuard lg; boost::shared_ptr c = channels.reader(); snprintf (buf, sizeof(buf), "%u", (unsigned int) c->size()); @@ -1827,12 +1932,12 @@ AudioDiskstream::get_state () int AudioDiskstream::set_state (const XMLNode& node, int version) { - const XMLProperty* prop; + XMLProperty const * prop; XMLNodeList nlist = node.children(); XMLNodeIterator niter; uint32_t nchans = 1; XMLNode* capture_pending_node = 0; - LocaleGuard lg (X_("POSIX")); + LocaleGuard lg; /* prevent write sources from being created */ @@ -1950,7 +2055,8 @@ AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/) if ((*chan)->write_source) { if (mark_write_complete) { - (*chan)->write_source->mark_streaming_write_completed (); + Source::Lock lock((*chan)->write_source->mutex()); + (*chan)->write_source->mark_streaming_write_completed (lock); (*chan)->write_source->done_with_peakfile_writes (); } @@ -2012,7 +2118,7 @@ AudioDiskstream::allocate_temporary_buffers () when slaving to MTC, Timecode etc. */ - double const sp = max (fabsf (_actual_speed), 1.2f); + double const sp = max (fabs (_actual_speed), 1.2); framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() * sp) + 2; if (required_wrap_size > wrap_buffer_size) { @@ -2131,11 +2237,11 @@ AudioDiskstream::playback_buffer_load () const boost::shared_ptr c = channels.reader(); if (c->empty ()) { - return 0; + return 1.0; } return (float) ((double) c->front()->playback_buf->read_space()/ - (double) c->front()->playback_buf->bufsize()); + (double) c->front()->playback_buf->bufsize()); } float @@ -2144,7 +2250,7 @@ AudioDiskstream::capture_buffer_load () const boost::shared_ptr c = channels.reader(); if (c->empty ()) { - return 0; + return 1.0; } return (float) ((double) c->front()->capture_buf->write_space()/ @@ -2154,7 +2260,7 @@ AudioDiskstream::capture_buffer_load () const int AudioDiskstream::use_pending_capture_data (XMLNode& node) { - const XMLProperty* prop; + XMLProperty const * prop; XMLNodeList nlist = node.children(); XMLNodeIterator niter; boost::shared_ptr fs; @@ -2178,8 +2284,8 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) } // This protects sessions from errant CapturingSources in stored sessions - struct stat sbuf; - if (stat (prop->value().c_str(), &sbuf)) { + GStatBuf sbuf; + if (g_stat (prop->value().c_str(), &sbuf)) { continue; } @@ -2227,11 +2333,11 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) boost::shared_ptr wf_region; boost::shared_ptr region; - + /* First create the whole file region */ PropertyList plist; - + plist.add (Properties::start, 0); plist.add (Properties::length, first_fs->length (first_fs->timeline_position())); plist.add (Properties::name, region_name_from_path (first_fs->name(), true)); @@ -2246,7 +2352,7 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) * the playlist */ region = boost::dynamic_pointer_cast (RegionFactory::create (pending_sources, plist)); - + _playlist->add_region (region, position); } @@ -2304,11 +2410,22 @@ AudioDiskstream::set_destructive (bool yn) bool AudioDiskstream::can_become_destructive (bool& requires_bounce) const { + if (Profile->get_trx()) { + return false; + } + if (!_playlist) { requires_bounce = false; return false; } + /* if no regions are present: easy */ + + if (_playlist->n_regions() == 0) { + requires_bounce = false; + return true; + } + /* is there only one region ? */ if (_playlist->n_regions() != 1) { @@ -2316,7 +2433,14 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const return false; } - boost::shared_ptr first = _playlist->find_next_region (_session.current_start_frame(), Start, 1); + boost::shared_ptr first; + { + const RegionList& rl (_playlist->region_list_property().rlist()); + assert((rl.size() == 1)); + first = rl.front(); + + } + if (!first) { requires_bounce = false; return true; @@ -2325,12 +2449,24 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const /* do the source(s) for the region cover the session start position ? */ if (first->position() != _session.current_start_frame()) { + // what is the idea here? why start() ?? if (first->start() > _session.current_start_frame()) { requires_bounce = true; return false; } } + /* currently RouteTimeAxisView::set_track_mode does not + * implement bounce. Existing regions cannot be converted. + * + * so let's make sure this region is already set up + * as tape-track (spanning the complete range) + */ + if (first->length() != max_framepos - first->position()) { + requires_bounce = true; + return false; + } + /* is the source used by only 1 playlist ? */ boost::shared_ptr afirst = boost::dynamic_pointer_cast (first); @@ -2430,6 +2566,24 @@ AudioDiskstream::ChannelInfo::resize_capture (framecnt_t capture_bufsize) AudioDiskstream::ChannelInfo::~ChannelInfo () { + if (write_source) { + if (write_source->removable()) { + /* this is a "stub" write source which exists in the + Session source list, but is removable. We must emit + a drop references call because it should not + continue to exist. If we do not do this, then the + Session retains a reference to it, it is not + deleted, and later attempts to create a new source + file will use wierd naming because it already + exists. + + XXX longer term TO-DO: do not add to session source + list until we write to the source. + */ + write_source->drop_references (); + } + } + write_source.reset (); delete [] speed_buffer;