/*
- Copyright (C) 2009-2016 Paul Davis
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
+ * Copyright (C) 2017-2018 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2017 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2018 Ben Loftis <ben@harrisonconsoles.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
#include "ardour/analyser.h"
#include "ardour/audioengine.h"
: DiskIOProcessor (s, str, f)
, _record_enabled (0)
, _record_safe (0)
- , capture_start_sample (0)
- , capture_captured (0)
- , was_recording (false)
- , first_recordable_sample (max_samplepos)
- , last_recordable_sample (max_samplepos)
- , last_possibly_recording (0)
+ , _capture_start_sample (0)
+ , _capture_captured (0)
+ , _was_recording (false)
+ , _first_recordable_sample (max_samplepos)
+ , _last_recordable_sample (max_samplepos)
+ , _last_possibly_recording (0)
, _alignment_style (ExistingMaterial)
+ , _note_mode (Sustained)
+ , _samples_pending_write (0)
, _num_captured_loops (0)
, _accumulated_capture_offset (0)
, _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
return 65536;
}
+void
+DiskWriter::WriterChannelInfo::resize (samplecnt_t bufsize)
+{
+ if (!capture_transition_buf) {
+ capture_transition_buf = new RingBufferNPT<CaptureTransition> (256);
+ }
+ delete wbuf;
+ wbuf = new RingBufferNPT<Sample> (bufsize);
+ /* touch memory to lock it */
+ memset (wbuf->buffer(), 0, sizeof (Sample) * wbuf->bufsize());
+}
+
+int
+DiskWriter::add_channel_to (boost::shared_ptr<ChannelList> c, uint32_t how_many)
+{
+ while (how_many--) {
+ c->push_back (new WriterChannelInfo (_session.butler()->audio_diskstream_capture_buffer_size()));
+ DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: new writer channel, write space = %2 read = %3\n",
+ name(),
+ c->back()->wbuf->write_space(),
+ c->back()->wbuf->read_space()));
+ }
+
+ return 0;
+}
+
+
+
bool
DiskWriter::set_write_source_name (string const & str)
{
possibly_recording = (speed != 0.0f ? 4 : 0) | (record_enabled() ? 2 : 0) | (can_record ? 1 : 0);
- if (possibly_recording == last_possibly_recording) {
+ if (possibly_recording == _last_possibly_recording) {
return;
}
if (possibly_recording == fully_rec_enabled) {
- if (last_possibly_recording == fully_rec_enabled) {
+ if (_last_possibly_recording == fully_rec_enabled) {
return;
}
Location* loc;
if (_session.config.get_punch_in () && 0 != (loc = _session.locations()->auto_punch_location ())) {
- capture_start_sample = loc->start ();
+ _capture_start_sample = loc->start ();
} else {
- capture_start_sample = _session.transport_sample ();
+ _capture_start_sample = _session.transport_sample ();
}
- first_recordable_sample = capture_start_sample;
+ _first_recordable_sample = _capture_start_sample;
if (_alignment_style == ExistingMaterial) {
- first_recordable_sample += _capture_offset + _playback_offset;
+ _first_recordable_sample += _capture_offset + _playback_offset;
}
if (_session.config.get_punch_out () && 0 != (loc = _session.locations()->auto_punch_location ())) {
* We should allow to move it or at least allow to disable punch-out
* while rolling..
*/
- last_recordable_sample = loc->end ();
+ _last_recordable_sample = loc->end ();
if (_alignment_style == ExistingMaterial) {
- last_recordable_sample += _capture_offset + _playback_offset;
+ _last_recordable_sample += _capture_offset + _playback_offset;
}
} else {
- last_recordable_sample = max_samplepos;
+ _last_recordable_sample = max_samplepos;
}
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,
+ _capture_start_sample,
+ _first_recordable_sample,
+ _last_recordable_sample,
_input_latency,
_output_latency,
_capture_offset,
_session.worst_input_latency()));
- prepare_record_status (capture_start_sample);
+ prepare_record_status (_capture_start_sample);
}
- last_possibly_recording = possibly_recording;
+ _last_possibly_recording = possibly_recording;
}
void
/* |--------| recrange
* -----| transrange
*/
- rec_nframes = transport_sample + nframes - first_recordable_sample;
+ rec_nframes = transport_sample + nframes - _first_recordable_sample;
if (rec_nframes) {
- rec_offset = first_recordable_sample - transport_sample;
+ rec_offset = _first_recordable_sample - transport_sample;
}
break;
/* |--------| recrange
* |-------- transrange
*/
- rec_nframes = last_recordable_sample - transport_sample;
+ rec_nframes = _last_recordable_sample - transport_sample;
rec_offset = 0;
break;
/* |--------| recrange
* -------------- transrange
*/
- rec_nframes = last_recordable_sample - first_recordable_sample;
- rec_offset = first_recordable_sample - transport_sample;
+ rec_nframes = _last_recordable_sample - _first_recordable_sample;
+ rec_offset = _first_recordable_sample - transport_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));
+ _first_recordable_sample, _last_recordable_sample, rec_nframes, rec_offset));
}
void
return capture_info[n]->start;
} else {
/* this is the currently in-progress capture */
- return capture_start_sample;
+ return _capture_start_sample;
}
}
return capture_info[n]->samples;
} else {
/* this is the currently in-progress capture */
- return capture_captured;
+ return _capture_captured;
}
}
}
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_("record-safe"), (_record_safe ? X_("yes" : "no")));
return node;
DiskWriter::non_realtime_locate (samplepos_t position)
{
if (_midi_write_source) {
- _midi_write_source->set_timeline_position (position);
+ _midi_write_source->set_natural_position (position);
}
DiskIOProcessor::non_realtime_locate (position);
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 ();
if (transitions.len[0] > 0) {
transitions.buf[0]->type = CaptureStart;
- transitions.buf[0]->capture_val = capture_start_sample;
+ transitions.buf[0]->capture_val = _capture_start_sample;
(*chan)->capture_transition_buf->increment_write_ptr(1);
} else {
// bad!
bool re = record_enabled ();
bool punch_in = _session.config.get_punch_in () && _session.locations()->auto_punch_location ();
bool can_record = _session.actively_recording ();
- can_record |= _session.get_record_enabled () && punch_in && _session.transport_sample () <= _session.locations()->auto_punch_location ()->start ();
+ can_record |= speed != 0 && _session.get_record_enabled () && punch_in && _session.transport_sample () <= _session.locations()->auto_punch_location ()->start ();
_need_butler = false;
// Safeguard against situations where process() goes haywire when autopunching
// and last_recordable_sample < first_recordable_sample
- if (last_recordable_sample < first_recordable_sample) {
- last_recordable_sample = max_samplepos;
+ if (_last_recordable_sample < _first_recordable_sample) {
+ _last_recordable_sample = max_samplepos;
}
- const Location* const loop_loc = loop_location;
+ const Location* const loop_loc = _loop_location;
samplepos_t loop_start = 0;
samplepos_t loop_end = 0;
samplepos_t loop_length = 0;
get_location_times (loop_loc, &loop_start, &loop_end, &loop_length);
}
- if (nominally_recording || (re && was_recording && _session.get_record_enabled() && punch_in)) {
+ 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);
+ 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
// XXX also, first_recordable_sample & last_recordable_sample may both be == max_samplepos: coverage() will return OverlapNone in that case. Is thak OK?
calculate_record_range (ot, start_sample, nframes, rec_nframes, rec_offset);
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: this time record %2 of %3 samples, offset %4\n", _name, rec_nframes, nframes, rec_offset));
- if (rec_nframes && !was_recording) {
- capture_captured = 0;
+ if (rec_nframes && !_was_recording) {
+ _capture_captured = 0;
if (loop_loc) {
/* Loop recording, so pretend the capture started at the loop
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_start_sample = loop_start;
+ _capture_captured = start_sample - loop_start;
+ _capture_start_sample = loop_start;
}
if (_midi_write_source) {
- _midi_write_source->mark_write_starting_now (capture_start_sample, capture_captured, loop_length);
+ _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);
- was_recording = true;
+ _was_recording = true;
}
ChannelInfo* chaninfo (*chan);
AudioBuffer& buf (bufs.get_audio (n%n_buffers));
- chaninfo->buf->get_write_vector (&chaninfo->rw_vector);
+ chaninfo->wbuf->get_write_vector (&chaninfo->rw_vector);
if (rec_nframes <= (samplecnt_t) chaninfo->rw_vector.len[0]) {
memcpy (chaninfo->rw_vector.buf[1], incoming + first, sizeof (Sample) * (rec_nframes - first));
}
- chaninfo->buf->increment_write_ptr (rec_nframes);
+ chaninfo->wbuf->increment_write_ptr (rec_nframes);
}
/* MIDI */
- // Pump entire port buffer into the ring buffer (TODO: split cycles?)
- MidiBuffer& buf = bufs.get_midi (0);
- boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack>(_route);
- MidiChannelFilter* filter = mt ? &mt->capture_filter() : 0;
+ if (_midi_buf) {
- for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
- Evoral::Event<MidiBuffer::TimeType> ev (*i, false);
- if (ev.time() + rec_offset > rec_nframes) {
- break;
- }
+ // Pump entire port buffer into the ring buffer (TODO: split cycles?)
+ MidiBuffer& buf = bufs.get_midi (0);
+ boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack>(_track);
+ MidiChannelFilter* filter = mt ? &mt->capture_filter() : 0;
+
+ assert (buf.size() == 0 || _midi_buf);
+
+ for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
+ Evoral::Event<MidiBuffer::TimeType> ev (*i, false);
+ if (ev.time() + rec_offset > rec_nframes) {
+ break;
+ }
#ifndef NDEBUG
- if (DEBUG_ENABLED(DEBUG::MidiIO)) {
- const uint8_t* __data = ev.buffer();
- DEBUG_STR_DECL(a);
- DEBUG_STR_APPEND(a, string_compose ("mididiskstream %1 capture event @ %2 + %3 sz %4 ", this, ev.time(), start_sample, ev.size()));
- for (size_t i=0; i < ev.size(); ++i) {
- DEBUG_STR_APPEND(a,hex);
- DEBUG_STR_APPEND(a,"0x");
- DEBUG_STR_APPEND(a,(int)__data[i]);
- DEBUG_STR_APPEND(a,' ');
+ if (DEBUG_ENABLED(DEBUG::MidiIO)) {
+ const uint8_t* __data = ev.buffer();
+ DEBUG_STR_DECL(a);
+ DEBUG_STR_APPEND(a, string_compose ("mididiskstream %1 capture event @ %2 + %3 sz %4 ", this, ev.time(), start_sample, ev.size()));
+ for (size_t i=0; i < ev.size(); ++i) {
+ DEBUG_STR_APPEND(a,hex);
+ DEBUG_STR_APPEND(a,"0x");
+ DEBUG_STR_APPEND(a,(int)__data[i]);
+ DEBUG_STR_APPEND(a,' ');
+ }
+ DEBUG_STR_APPEND(a,'\n');
+ DEBUG_TRACE (DEBUG::MidiIO, DEBUG_STR(a).str());
}
- DEBUG_STR_APPEND(a,'\n');
- DEBUG_TRACE (DEBUG::MidiIO, DEBUG_STR(a).str());
- }
#endif
- /* Write events to the capture buffer in samples from session start,
- but ignoring looping so event time progresses monotonically.
- The source knows the loop length so it knows exactly where the
- event occurs in the series of recorded loops and can implement
- any desirable behaviour. We don't want to send event with
- transport time here since that way the source can not
- reconstruct their actual time; future clever MIDI looping should
- probably be implemented in the source instead of here.
- */
- const samplecnt_t loop_offset = _num_captured_loops * loop_length;
- const samplepos_t event_time = start_sample + loop_offset - _accumulated_capture_offset + ev.time();
- if (event_time < 0 || event_time < first_recordable_sample) {
- /* Event out of range, skip */
- continue;
- }
+ /* Write events to the capture buffer in samples from session start,
+ but ignoring looping so event time progresses monotonically.
+ The source knows the loop length so it knows exactly where the
+ event occurs in the series of recorded loops and can implement
+ any desirable behaviour. We don't want to send event with
+ transport time here since that way the source can not
+ reconstruct their actual time; future clever MIDI looping should
+ probably be implemented in the source instead of here.
+ */
+ const samplecnt_t loop_offset = _num_captured_loops * loop_length;
+ const samplepos_t event_time = start_sample + loop_offset - _accumulated_capture_offset + ev.time();
+ if (event_time < 0 || event_time < _first_recordable_sample) {
+ /* Event out of range, skip */
+ 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());
+ 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);
- if (buf.size() != 0) {
- Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK);
+ g_atomic_int_add (const_cast<gint*>(&_samples_pending_write), nframes);
- if (lm.locked ()) {
- /* Copy this data into our GUI feed buffer and tell the GUI
- that it can read it if it likes.
- */
- _gui_feed_buffer.clear ();
+ if (buf.size() != 0) {
+ Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK);
- for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
- /* This may fail if buf is larger than _gui_feed_buffer, but it's not really
- the end of the world if it does.
+ if (lm.locked ()) {
+ /* Copy this data into our GUI feed buffer and tell the GUI
+ that it can read it if it likes.
*/
- _gui_feed_buffer.push_back ((*i).time() + start_sample, (*i).size(), (*i).buffer());
+ _gui_feed_buffer.clear ();
+
+ for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
+ /* This may fail if buf is larger than _gui_feed_buffer, but it's not really
+ the end of the world if it does.
+ */
+ _gui_feed_buffer.push_back ((*i).time() + start_sample, (*i).size(), (*i).buffer());
+ }
}
- }
- DataRecorded (_midi_write_source); /* EMIT SIGNAL */
+ DataRecorded (_midi_write_source); /* EMIT SIGNAL */
+ }
}
- capture_captured += rec_nframes;
- DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 now captured %2 (by %3)\n", name(), capture_captured, rec_nframes));
+ _capture_captured += rec_nframes;
+ DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 now captured %2 (by %3)\n", name(), _capture_captured, rec_nframes));
} else {
/* not recording this time, but perhaps we were before .. */
- if (was_recording) {
+ if (_was_recording) {
finish_capture (c);
_accumulated_capture_offset = 0;
}
/* AUDIO BUTLER REQUIRED CODE */
if (_playlists[DataType::AUDIO] && !c->empty()) {
- if (((samplecnt_t) c->front()->buf->read_space() >= _chunk_samples)) {
+ if (((samplecnt_t) c->front()->wbuf->read_space() >= _chunk_samples)) {
_need_butler = true;
}
}
/* MIDI BUTLER REQUIRED CODE */
- if (_playlists[DataType::MIDI] && (_midi_buf->read_space() < _midi_buf->bufsize() / 2)) {
+ if (_playlists[DataType::MIDI] && _midi_buf && (_midi_buf->read_space() < _midi_buf->bufsize() / 2)) {
_need_butler = true;
}
void
DiskWriter::finish_capture (boost::shared_ptr<ChannelList> c)
{
- was_recording = false;
- first_recordable_sample = max_samplepos;
- last_recordable_sample = max_samplepos;
+ _was_recording = false;
+ _first_recordable_sample = max_samplepos;
+ _last_recordable_sample = max_samplepos;
- if (capture_captured == 0) {
+ if (_capture_captured == 0) {
return;
}
if (transvec.len[0] > 0) {
transvec.buf[0]->type = CaptureEnd;
- transvec.buf[0]->capture_val = capture_captured;
+ transvec.buf[0]->capture_val = _capture_captured;
(*chan)->capture_transition_buf->increment_write_ptr(1);
}
else {
CaptureInfo* ci = new CaptureInfo;
- ci->start = capture_start_sample;
- ci->samples = capture_captured;
+ ci->start = _capture_start_sample;
+ ci->samples = _capture_captured;
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("Finish capture, add new CI, %1 + %2\n", ci->start, ci->samples));
*/
capture_info.push_back (ci);
- capture_captured = 0;
+ _capture_captured = 0;
/* now we've finished a capture, reset first_recordable_sample for next time */
- first_recordable_sample = max_samplepos;
+ _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
return 1.0;
}
- return (float) ((double) c->front()->buf->write_space()/
- (double) c->front()->buf->bufsize());
+ return (float) ((double) c->front()->wbuf->write_space()/
+ (double) c->front()->wbuf->bufsize());
}
void
boost::shared_ptr<ChannelList> c = channels.reader();
for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
- (*chan)->buf->reset ();
+ (*chan)->wbuf->reset ();
}
- _midi_buf->reset ();
+ if (_midi_buf) {
+ _midi_buf->reset ();
+ }
g_atomic_int_set(&_samples_read_from_ringbuffer, 0);
g_atomic_int_set(&_samples_written_to_ringbuffer, 0);
boost::shared_ptr<ChannelList> c = channels.reader();
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
- (*chan)->buf->get_read_vector (&vector);
+ (*chan)->wbuf->get_read_vector (&vector);
total = vector.len[0] + vector.len[1];
- if (total == 0 || (total < _chunk_samples && !force_flush && was_recording)) {
+ if (total == 0 || (total < _chunk_samples && !force_flush && _was_recording)) {
goto out;
}
let the caller know too.
*/
- if (total >= 2 * _chunk_samples || ((force_flush || !was_recording) && total > _chunk_samples)) {
+ if (total >= 2 * _chunk_samples || ((force_flush || !_was_recording) && total > _chunk_samples)) {
ret = 1;
}
return -1;
}
- (*chan)->buf->increment_read_ptr (to_write);
+ (*chan)->wbuf->increment_read_ptr (to_write);
(*chan)->curr_capture_cnt += to_write;
if ((to_write == vector.len[0]) && (total > to_write) && (to_write < _chunk_samples) && !destructive()) {
return -1;
}
- (*chan)->buf->increment_read_ptr (to_write);
+ (*chan)->wbuf->increment_read_ptr (to_write);
(*chan)->curr_capture_cnt += to_write;
}
}
/* MIDI*/
- if (_midi_write_source) {
+ if (_midi_write_source && _midi_buf) {
const samplecnt_t total = g_atomic_int_get(const_cast<gint*> (&_samples_pending_write));
if (total == 0 ||
_midi_buf->read_space() == 0 ||
- (!force_flush && (total < _chunk_samples) && was_recording)) {
+ (!force_flush && (total < _chunk_samples) && _was_recording)) {
goto out;
}
let the caller know too.
*/
- if (total >= 2 * _chunk_samples || ((force_flush || !was_recording) && total > _chunk_samples)) {
+ if (total >= 2 * _chunk_samples || ((force_flush || !_was_recording) && total > _chunk_samples)) {
ret = 1;
}
as->set_captured_for (_name.val());
as->mark_immutable ();
+ char buf[128];
+ strftime (buf, sizeof(buf), "%F %H.%M.%S", &when);
+ as->set_take_id ( buf );
+
if (Config->get_auto_analyse_audio()) {
Analyser::queue_source_for_analysis (as, true);
}
if (_midi_write_source) {
midi_srcs.push_back (_midi_write_source);
}
+
+ (*chan)->write_source->stamp (twhen);
+
+ /* "re-announce the source to the world */
+ Source::SourcePropertyChanged ((*chan)->write_source);
}
midi_srcs.push_back (_midi_write_source);
- _midi_write_source->set_timeline_position (capture_info.front()->start);
+ _midi_write_source->set_natural_position (capture_info.front()->start);
_midi_write_source->set_captured_for (_name);
+ char buf[128];
+ strftime (buf, sizeof(buf), "%F %H.%M.%S", &when);
+ _midi_write_source->set_take_id ( buf );
+
/* set length in beats to entire capture length */
BeatsSamplesConverter converter (_session.tempo_map(), capture_info.front()->start);
_last_capture_sources.insert (_last_capture_sources.end(), midi_srcs.begin(), midi_srcs.end());
- if (_route) {
- _route->use_captured_sources (audio_srcs, capture_info);
- _route->use_captured_sources (midi_srcs, capture_info);
+ if (_track) {
+ _track->use_captured_sources (audio_srcs, capture_info);
+ _track->use_captured_sources (midi_srcs, capture_info);
}
mark_write_completed = true;
}
capture_info.clear ();
- capture_start_sample = 0;
+ _capture_start_sample = 0;
}
void
DiskWriter::transport_looped (samplepos_t transport_sample)
{
- if (was_recording) {
+ if (_was_recording) {
// all we need to do is finish this capture, with modified capture length
boost::shared_ptr<ChannelList> c = channels.reader();
// the next region will start recording via the normal mechanism
// we'll set the start position to the current transport pos
// no latency adjustment or capture offset needs to be made, as that already happened the first time
- capture_start_sample = transport_sample;
- first_recordable_sample = transport_sample; // mild lie
- last_recordable_sample = max_samplepos;
- was_recording = true;
+ _capture_start_sample = transport_sample;
+ _first_recordable_sample = transport_sample; // mild lie
+ _last_recordable_sample = max_samplepos;
+ _was_recording = true;
if (recordable() && destructive()) {
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
if (transvec.len[0] > 0) {
transvec.buf[0]->type = CaptureStart;
- transvec.buf[0]->capture_val = capture_start_sample;
+ transvec.buf[0]->capture_val = _capture_start_sample;
(*chan)->capture_transition_buf->increment_write_ptr(1);
}
else {
we want to implement more clever MIDI looping modes it should be done in
the Source and/or entirely after the capture is finished.
*/
- if (was_recording) {
+ if (_was_recording) {
g_atomic_int_add(const_cast<gint*> (&_num_captured_loops), 1);
}
}
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
bool
DiskWriter::configure_io (ChanCount in, ChanCount out)
{
+ bool changed = false;
+ {
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ if (in.n_audio() != c->size()) {
+ changed = true;
+ }
+ if ((0 == in.n_midi ()) != (0 == _midi_buf)) {
+ changed = true;
+ }
+ }
+
+
if (!DiskIOProcessor::configure_io (in, out)) {
return false;
}
- reset_write_sources (false, true);
+ if (record_enabled() || changed) {
+ reset_write_sources (false, true);
+ }
return true;
}