Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <fstream>
#include <cstdio>
#include <unistd.h>
#include <cmath>
#include <fcntl.h>
#include <cstdlib>
#include <ctime>
-#include <sys/stat.h>
-#include <sys/mman.h>
+#include "pbd/gstdio_compat.h"
#include "pbd/error.h"
#include "pbd/xml++.h"
#include "pbd/memento_command.h"
#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"
using namespace ARDOUR;
using namespace PBD;
-size_t AudioDiskstream::_working_buffers_size = 0;
Sample* AudioDiskstream::_mixdown_buffer = 0;
gain_t* AudioDiskstream::_gain_buffer = 0;
in_set_state = true;
use_new_playlist ();
in_set_state = false;
+
+ if (flag & Destructive) {
+ use_destructive_playlist ();
+ }
}
AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
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
{
delete [] _mixdown_buffer;
delete [] _gain_buffer;
- _working_buffers_size = 0;
_mixdown_buffer = 0;
_gain_buffer = 0;
}
void
AudioDiskstream::non_realtime_input_change ()
{
+ bool need_write_sources = false;
+
{
Glib::Threads::Mutex::Lock lm (state_lock);
return;
}
- if (input_change_pending.type == IOChange::ConfigurationChanged) {
+ boost::shared_ptr<ChannelList> cr = channels.reader();
+ if (!cr->empty() && !cr->front()->write_source) {
+ need_write_sources = true;
+ }
+
+ if (input_change_pending.type & IOChange::ConfigurationChanged) {
RCUWriter<ChannelList> writer (channels);
boost::shared_ptr<ChannelList> c = writer.get_copy();
} else if (_io->n_ports().n_audio() < _n_channels.n_audio()) {
remove_channel_from (c, _n_channels.n_audio() - _io->n_ports().n_audio());
}
+
+ need_write_sources = true;
}
if (input_change_pending.type & IOChange::ConnectionsChanged) {
/* implicit unlock */
}
- /* reset capture files */
-
- reset_write_sources (false);
+ if (need_write_sources) {
+ reset_write_sources (false);
+ }
/* now refill channel buffers */
/* 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);
}
}
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 ();
}
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> region (RegionFactory::create (srcs, plist));
_playlist->add_region (region, srcs.front()->natural_position());
+
+ /* apply region properties and update write sources */
+ use_destructive_playlist();
}
void
with the (presumed single, full-extent) region.
*/
- boost::shared_ptr<Region> rp = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
+ boost::shared_ptr<Region> rp;
+ {
+ const RegionList& rl (_playlist->region_list().rlist());
+ if (rl.size() > 0) {
+ assert((rl.size() == 1));
+ rp = rl.front();
+ }
+ }
if (!rp) {
reset_write_sources (false, true);
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;
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;
}
}
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;
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;
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],
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;
}
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);
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 */
}
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;
}
}
} 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);
}
}
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;
boost::shared_ptr<ChannelList> c = channels.reader();
for (chan = c->begin(); chan != c->end(); ++chan) {
- (*chan)->playback_buf->increment_read_ptr (std::llabs(distance));
+ (*chan)->playback_buf->increment_read_ptr (::llabs(distance));
}
if (first_recordable_frame < max_framepos) {
if (loc && start >= loop_end) {
start = loop_start + ((start - loop_start) % loop_length);
}
+
}
if (reversed) {
}
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;
/** 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;
boost::shared_ptr<ChannelList> 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;
}
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;
}
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) {
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);
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
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);
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)) {
}
+ // 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
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;
}
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
(*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;
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)));
}
}
_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 ();
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 {
// all we need to do is finish this capture, with modified capture length
boost::shared_ptr<ChannelList> 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
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;
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;
}
}
}
+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;
}
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);
}
}
{
XMLNode& node (Diskstream::get_state());
char buf[64] = "";
- LocaleGuard lg (X_("POSIX"));
+ LocaleGuard lg (X_("C"));
boost::shared_ptr<ChannelList> c = channels.reader();
- snprintf (buf, sizeof(buf), "%zd", c->size());
+ snprintf (buf, sizeof(buf), "%u", (unsigned int) c->size());
node.add_property ("channels", buf);
if (!capturing_sources.empty() && _session.get_record_enabled()) {
XMLNodeIterator niter;
uint32_t nchans = 1;
XMLNode* capture_pending_node = 0;
- LocaleGuard lg (X_("POSIX"));
+ LocaleGuard lg (X_("C"));
/* prevent write sources from being created */
try {
if ((chan->write_source = _session.create_audio_source_for_session (
- n_channels().n_audio(), name(), n, destructive())) == 0) {
+ n_channels().n_audio(), write_source_name(), n, destructive())) == 0) {
throw failed_constructor();
}
}
return 0;
}
-list<boost::shared_ptr<Source> >
-AudioDiskstream::steal_write_sources()
-{
- /* not possible to steal audio write sources */
- list<boost::shared_ptr<Source> > ret;
- return ret;
-}
-
void
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 ();
}
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) {
boost::shared_ptr<ChannelList> 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
boost::shared_ptr<ChannelList> c = channels.reader();
if (c->empty ()) {
- return 0;
+ return 1.0;
}
return (float) ((double) c->front()->capture_buf->write_space()/
}
// 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;
}
+ /* XXX as of June 2014, we always record to mono
+ files. Since this Source is being created as part of
+ crash recovery, we know that we need the first
+ channel (the final argument to the SourceFactory
+ call below). If we ever support non-mono files for
+ capture, this will need rethinking.
+ */
+
try {
- fs = boost::dynamic_pointer_cast<AudioFileSource> (
- SourceFactory::createWritable (
- DataType::AUDIO, _session,
- prop->value(), false, _session.frame_rate()));
+ fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createForRecovery (DataType::AUDIO, _session, prop->value(), 0));
}
catch (failed_constructor& err) {
return -1;
}
- boost::shared_ptr<AudioRegion> region;
-
try {
+ boost::shared_ptr<AudioRegion> wf_region;
+ boost::shared_ptr<AudioRegion> 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));
+ wf_region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
+
+ wf_region->set_automatic (true);
+ wf_region->set_whole_file (true);
+ wf_region->special_set_position (position);
+
+ /* Now create a region that isn't the whole file for adding to
+ * the playlist */
+
region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
- region->set_automatic (true);
- region->set_whole_file (true);
- region->special_set_position (0);
+ _playlist->add_region (region, position);
}
catch (failed_constructor& err) {
return -1;
}
- _playlist->add_region (region, position);
return 0;
}
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) {
return false;
}
- boost::shared_ptr<Region> first = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
+ boost::shared_ptr<Region> first;
+ {
+ const RegionList& rl (_playlist->region_list().rlist());
+ assert((rl.size() == 1));
+ first = rl.front();
+
+ }
+
if (!first) {
requires_bounce = false;
return true;
/* 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<AudioRegion> afirst = boost::dynamic_pointer_cast<AudioRegion> (first);
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;
bool
AudioDiskstream::set_name (string const & name)
{
+ if (_name == name) {
+ return true;
+ }
Diskstream::set_name (name);
/* get a new write source so that its name reflects the new diskstream name */
return true;
}
+
+bool
+AudioDiskstream::set_write_source_name (const std::string& str) {
+ if (_write_source_name == str) {
+ return true;
+ }
+
+ Diskstream::set_write_source_name (str);
+
+ if (_write_source_name == name()) {
+ return true;
+ }
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ ChannelList::iterator i;
+ int n = 0;
+
+ for (n = 0, i = c->begin(); i != c->end(); ++i, ++n) {
+ use_new_write_source (n);
+ }
+ return true;
+}