*/
-#include "pbd/i18n.h"
-
#include "ardour/analyser.h"
#include "ardour/audioengine.h"
#include "ardour/audiofilesource.h"
#include "ardour/session.h"
#include "ardour/smf_source.h"
+#include "pbd/i18n.h"
+
using namespace ARDOUR;
using namespace PBD;
using namespace std;
, _record_enabled (0)
, _record_safe (0)
, capture_start_sample (0)
- , capture_captured (0)
- , was_recording (false)
- , adjust_capture_position (0)
- , _capture_offset (0)
- , first_recordable_sample (max_samplepos)
- , last_recordable_sample (max_samplepos)
- , last_possibly_recording (0)
- , _alignment_style (ExistingMaterial)
- , _alignment_choice (Automatic)
+ , capture_captured (0)
+ , was_recording (false)
+ , first_recordable_sample (max_samplepos)
+ , last_recordable_sample (max_samplepos)
+ , last_possibly_recording (0)
+ , _alignment_style (ExistingMaterial)
, _num_captured_loops (0)
, _accumulated_capture_offset (0)
, _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
}
void
-DiskWriter::check_record_status (samplepos_t transport_sample, bool can_record)
+DiskWriter::check_record_status (samplepos_t transport_sample, double speed, bool can_record)
{
int possibly_recording;
- int rolling;
- int change;
const int transport_rolling = 0x4;
const int track_rec_enabled = 0x2;
const int global_rec_enabled = 0x1;
- const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled);
+ const int fully_rec_enabled = (transport_rolling |track_rec_enabled | global_rec_enabled);
- /* merge together the 3 factors that affect record status, and compute
- * what has changed.
- */
+ /* merge together the 3 factors that affect record status, and compute what has changed. */
- rolling = _session.transport_speed() != 0.0f;
- possibly_recording = (rolling << 2) | ((int)record_enabled() << 1) | (int)can_record;
- change = possibly_recording ^ last_possibly_recording;
+ possibly_recording = (speed != 0.0f ? 4 : 0) | (record_enabled() ? 2 : 0) | (can_record ? 1 : 0);
if (possibly_recording == last_possibly_recording) {
return;
}
- const samplecnt_t existing_material_offset = _session.worst_playback_latency();
-
if (possibly_recording == fully_rec_enabled) {
if (last_possibly_recording == fully_rec_enabled) {
return;
}
- capture_start_sample = _session.transport_sample();
- first_recordable_sample = capture_start_sample + _capture_offset;
- last_recordable_sample = max_samplepos;
-
- DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %7 (%9) FRF = %2 CSF = %4 CO = %5, EMO = %6 RD = %8 WOL %10 WTL %11\n",
- name(), first_recordable_sample, last_recordable_sample, capture_start_sample,
- _capture_offset,
- existing_material_offset,
- transport_sample,
- _session.transport_sample(),
- _session.worst_output_latency(),
- _session.worst_track_latency()));
-
-
- if (_alignment_style == ExistingMaterial) {
- first_recordable_sample += existing_material_offset;
- DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tshift FRF by EMO %1\n",
- first_recordable_sample));
- }
-
- prepare_record_status (capture_start_sample);
-
- } else {
+ Location* loc;
+ if (_session.config.get_punch_in () && 0 != (loc = _session.locations()->auto_punch_location ())) {
+ capture_start_sample = loc->start ();
+ } else {
+ capture_start_sample = _session.transport_sample ();
+ }
- if (last_possibly_recording == fully_rec_enabled) {
+ first_recordable_sample = capture_start_sample;
- /* we were recording last time */
+ if (_alignment_style == ExistingMaterial) {
+ first_recordable_sample += _capture_offset + _playback_offset;
+ }
- if (change & transport_rolling) {
+ if (_session.config.get_punch_out () && 0 != (loc = _session.locations()->auto_punch_location ())) {
+ /* this freezes the punch-out point when starting to record.
+ *
+ * We should allow to move it or at least allow to disable punch-out
+ * while rolling..
+ */
+ last_recordable_sample = loc->end ();
+ if (_alignment_style == ExistingMaterial) {
+ last_recordable_sample += _capture_offset + _playback_offset;
+ }
+ } else {
+ last_recordable_sample = max_samplepos;
+ }
- /* transport-change (stopped rolling): last_recordable_sample was set in ::prepare_to_stop(). We
- * had to set it there because we likely rolled past the stopping point to declick out,
- * and then backed up.
- */
+ DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %2 (STS: %3) CS:%4 FRS: %5 IL: %7, OL: %8 CO: %r9 PO: %10 WOL: %11 WIL: %12\n",
+ name(),
+ transport_sample,
+ _session.transport_sample(),
+ capture_start_sample,
+ first_recordable_sample,
+ last_recordable_sample,
+ _input_latency,
+ _output_latency,
+ _capture_offset,
+ _playback_offset,
+ _session.worst_output_latency(),
+ _session.worst_input_latency()));
- } else {
- /* punch out */
- last_recordable_sample = _session.transport_sample() + _capture_offset;
+ prepare_record_status (capture_start_sample);
- if (_alignment_style == ExistingMaterial) {
- last_recordable_sample += existing_material_offset;
- }
- }
- }
}
last_possibly_recording = possibly_recording;
}
void
-DiskWriter::calculate_record_range (Evoral::OverlapType ot, samplepos_t transport_sample, samplecnt_t nframes,
- samplecnt_t & rec_nframes, samplecnt_t & rec_offset)
+DiskWriter::calculate_record_range (Evoral::OverlapType ot, samplepos_t transport_sample, samplecnt_t nframes, samplecnt_t & rec_nframes, samplecnt_t & rec_offset)
{
switch (ot) {
case Evoral::OverlapNone:
break;
}
- DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 rec? %2 @ %3 (for %4) FRF %5 LRF %6 : rf %7 @ %8\n",
- _name, enum_2_string (ot), transport_sample, nframes,
- first_recordable_sample, last_recordable_sample, rec_nframes, rec_offset));
-}
-
-void
-DiskWriter::prepare_to_stop (samplepos_t transport_sample, samplepos_t audible_sample)
-{
- switch (_alignment_style) {
- case ExistingMaterial:
- last_recordable_sample = transport_sample + _capture_offset;
- DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable sample to %2 + %3 = %4\n", _name, transport_sample, _capture_offset, last_recordable_sample));
- break;
-
- case CaptureTime:
- last_recordable_sample = audible_sample; // note that capture_offset is zero
- /* we may already have captured audio before the last_recordable_sample (audible sample),
- so deal with this.
- */
- if (last_recordable_sample > capture_start_sample) {
- capture_captured = min (capture_captured, last_recordable_sample - capture_start_sample);
- }
- DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable sample to audible sample @ %2\n", _name, audible_sample));
- break;
- }
-
+ DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 rec? %2 @ %3 (for %4) FRF %5 LRF %6 : rf %7 @ %8\n",
+ _name, enum_2_string (ot), transport_sample, nframes,
+ first_recordable_sample, last_recordable_sample, rec_nframes, rec_offset));
}
void
}
}
-void
-DiskWriter::set_input_latency (samplecnt_t l)
-{
- Processor::set_input_latency (l);
- set_capture_offset ();
-}
-
-void
-DiskWriter::set_capture_offset ()
-{
- switch (_alignment_style) {
- case ExistingMaterial:
- _capture_offset = _input_latency;
- break;
-
- case CaptureTime:
- default:
- _capture_offset = 0;
- break;
- }
-
- DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: using input latency %4, capture offset set to %2 with style = %3\n", name(), _capture_offset, enum_2_string (_alignment_style), _input_latency));
-}
-
-
void
DiskWriter::set_align_style (AlignStyle a, bool force)
{
if ((a != _alignment_style) || force) {
_alignment_style = a;
- set_capture_offset ();
AlignmentStyleChanged ();
}
}
-void
-DiskWriter::set_align_choice (AlignChoice a, bool force)
-{
- if (record_enabled() && _session.actively_recording()) {
- return;
- }
-
- if ((a != _alignment_choice) || force) {
- _alignment_choice = a;
-
- switch (_alignment_choice) {
- case UseExistingMaterial:
- set_align_style (ExistingMaterial);
- break;
- case UseCaptureTime:
- set_align_style (CaptureTime);
- break;
- default:
- error << string_compose (_("programming error: %1"), "DiskWriter: asked to use illegal alignment style") << endmsg;
- break;
- }
- }
-}
-
XMLNode&
-DiskWriter::state (bool full)
+DiskWriter::state ()
{
- XMLNode& node (DiskIOProcessor::state (full));
+ XMLNode& node (DiskIOProcessor::state ());
node.set_property (X_("type"), X_("diskwriter"));
- node.set_property (X_("capture-alignment"), enum_2_string (_alignment_choice));
node.set_property (X_("record-safe"), (_record_safe ? X_("yes" : "no")));
return node;
}
return -1;
}
- AlignChoice ac;
-
- if (node.get_property (X_("capture-alignment"), ac)) {
- set_align_choice (ac, true);
- } else {
- set_align_choice (Automatic, true);
- }
-
if (!node.get_property (X_("record-safe"), _record_safe)) {
_record_safe = false;
}
void
-DiskWriter::prepare_record_status(samplepos_t capture_start_sample)
+DiskWriter::prepare_record_status (samplepos_t capture_start_sample)
{
if (recordable() && destructive()) {
- boost::shared_ptr<ChannelList> c = channels.reader();
+ boost::shared_ptr<ChannelList> c = channels.reader ();
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
RingBufferNPT<CaptureTransition>::rw_vector transitions;
DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample,
double speed, pframes_t nframes, bool result_required)
{
+ if (!_active && !_pending_active) {
+ return;
+ }
+ _active = _pending_active;
+
uint32_t n;
boost::shared_ptr<ChannelList> c = channels.reader();
ChannelList::iterator chan;
+
samplecnt_t rec_offset = 0;
samplecnt_t rec_nframes = 0;
bool nominally_recording;
+
bool re = record_enabled ();
+ bool punch_in = _session.config.get_punch_in () && _session.locations()->auto_punch_location ();
bool can_record = _session.actively_recording ();
-
- if (_active) {
- if (!_pending_active) {
- _active = false;
- return;
- }
- } else {
- if (_pending_active) {
- _active = true;
- } else {
- return;
- }
- }
+ can_record |= speed != 0 && _session.get_record_enabled () && punch_in && _session.transport_sample () <= _session.locations()->auto_punch_location ()->start ();
_need_butler = false;
- check_record_status (start_sample, can_record);
+#ifndef NDEBUG
+ if (speed != 0 && re) {
+ DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: run() start: %2 end: %3 NF: %4\n", _name, start_sample, end_sample, nframes));
+ }
+#endif
+
+ check_record_status (start_sample, speed, can_record);
if (nframes == 0) {
return;
}
const Location* const loop_loc = loop_location;
- samplepos_t loop_start = 0;
- samplepos_t loop_end = 0;
- samplepos_t loop_length = 0;
+ samplepos_t loop_start = 0;
+ samplepos_t loop_end = 0;
+ samplepos_t loop_length = 0;
if (loop_loc) {
get_location_times (loop_loc, &loop_start, &loop_end, &loop_length);
}
- adjust_capture_position = 0;
-
- if (nominally_recording || (re && was_recording && _session.get_record_enabled() && (_session.config.get_punch_in() || _session.preroll_record_punch_enabled()))) {
+ if (nominally_recording || (re && was_recording && _session.get_record_enabled() && punch_in)) {
Evoral::OverlapType ot = Evoral::coverage (first_recordable_sample, last_recordable_sample, start_sample, end_sample);
// XXX should this be transport_sample + nframes - 1 ? coverage() expects its parameter ranges to include their end points
at the loop start and can handle time wrapping around.
Otherwise, start the source right now as usual.
*/
- capture_captured = start_sample - loop_start;
+ capture_captured = start_sample - loop_start;
capture_start_sample = loop_start;
}
_midi_write_source->mark_write_starting_now (capture_start_sample, capture_captured, loop_length);
}
- g_atomic_int_set(const_cast<gint*> (&_samples_pending_write), 0);
- g_atomic_int_set(const_cast<gint*> (&_num_captured_loops), 0);
+ g_atomic_int_set (const_cast<gint*> (&_samples_pending_write), 0);
+ g_atomic_int_set (const_cast<gint*> (&_num_captured_loops), 0);
was_recording = true;
}
- if (can_record && !_last_capture_sources.empty()) {
+ if (can_record && !_last_capture_sources.empty ()) {
_last_capture_sources.clear ();
}
samplecnt_t total = chaninfo->rw_vector.len[0] + chaninfo->rw_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));
- Overrun ();
+ DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 overrun in %2, rec_nframes = %3 total space = %4\n",
+ DEBUG_THREAD_SELF, name(), rec_nframes, total));
+ Overrun ();
return;
}
MidiChannelFilter* filter = mt ? &mt->capture_filter() : 0;
for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
- Evoral::Event<MidiBuffer::TimeType> ev(*i, false);
+ Evoral::Event<MidiBuffer::TimeType> ev (*i, false);
if (ev.time() + rec_offset > rec_nframes) {
break;
}
continue;
}
+ bool skip_event = false;
+ if (mt) {
+ /* skip injected immediate/out-of-band events */
+ MidiBuffer const& ieb (mt->immediate_event_buffer());
+ for (MidiBuffer::const_iterator j = ieb.begin(); j != ieb.end(); ++j) {
+ if (*j == ev) {
+ skip_event = true;
+ }
+ }
+ }
+ if (skip_event) {
+ continue;
+ }
+
if (!filter || !filter->filter(ev.buffer(), ev.size())) {
_midi_buf->write (event_time, ev.event_type(), ev.size(), ev.buffer());
}
}
- g_atomic_int_add(const_cast<gint*>(&_samples_pending_write), nframes);
+ g_atomic_int_add (const_cast<gint*>(&_samples_pending_write), nframes);
if (buf.size() != 0) {
Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK);
first_recordable_sample = max_samplepos;
}
+boost::shared_ptr<MidiBuffer>
+DiskWriter::get_gui_feed_buffer () const
+{
+ boost::shared_ptr<MidiBuffer> b (new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI)));
+
+ Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex);
+ b->copy (_gui_feed_buffer);
+ return b;
+}
+
void
DiskWriter::set_record_enabled (bool yn)
{
/* set length in beats to entire capture length */
BeatsSamplesConverter converter (_session.tempo_map(), capture_info.front()->start);
- const Evoral::Beats total_capture_beats = converter.from (total_capture);
+ const Temporal::Beats total_capture_beats = converter.from (total_capture);
_midi_write_source->set_length_beats (total_capture_beats);
/* flush to disk: this step differs from the audio path,
where all the data is already on disk.
*/
- _midi_write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence<Evoral::Beats>::ResolveStuckNotes, total_capture_beats);
+ _midi_write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence<Temporal::Beats>::ResolveStuckNotes, total_capture_beats);
}
_last_capture_sources.insert (_last_capture_sources.end(), audio_srcs.begin(), audio_srcs.end());
assert((*chan)->write_source);
(*chan)->write_source->set_allow_remove_if_empty (false);
- /* this might be false if we switched modes, so force it */
-
-#ifdef XXX_OLD_DESTRUCTIVE_API_XXX
- (*chan)->write_source->set_destructive (true);
-#else
// should be set when creating the source or loading the state
assert ((*chan)->write_source->destructive());
-#endif
}
/* the source list will never be reset for a destructive track */
void
DiskWriter::realtime_handle_transport_stopped ()
{
- realtime_speed_change ();
}
bool