13
0

first functioning MIDI clip recording (libs)

This commit is contained in:
Paul Davis 2024-09-25 18:34:28 -06:00
parent a2d44ba97d
commit ac4bb55f13
8 changed files with 392 additions and 77 deletions

View File

@ -72,7 +72,6 @@ namespace ARDOUR {
class Amp;
class BeatBox;
class DelayLine;
class ClipRecProcessor;
class Delivery;
class DiskReader;
class DiskWriter;
@ -628,7 +627,6 @@ protected:
std::shared_ptr<Pannable> _pannable;
std::shared_ptr<DiskReader> _disk_reader;
std::shared_ptr<DiskWriter> _disk_writer;
std::shared_ptr<ClipRecProcessor> _clip_recorder;
#ifdef HAVE_BEATBOX
std::shared_ptr<BeatBox> _beatbox;
#endif

View File

@ -56,7 +56,7 @@ class LIBARDOUR_API RTMidiBufferBase : public Evoral::EventSink<TimeType>
a reference to any data. The data is all "moved" to the returned
RTMidiBufferBase and timestamps modified to its time domain if nececssary.
*/
RTMidiBufferBase<Temporal::Beats,Temporal::Beats>* convert ();
void convert (RTMidiBufferBase<Temporal::Beats,Temporal::Beats>&);
void clear();
void resize(size_t);
@ -106,9 +106,6 @@ class LIBARDOUR_API RTMidiBufferBase : public Evoral::EventSink<TimeType>
*/
void shift (DistanceType distance) {
if (_size == 0) {
return;
}
for (size_t n = 0; n < _size; ++n) {
_data[n].timestamp += distance;
}
@ -152,18 +149,5 @@ class LIBARDOUR_API RTMidiBufferBase : public Evoral::EventSink<TimeType>
typedef RTMidiBufferBase<samplepos_t,samplecnt_t> RTMidiBuffer;
class Trigger;
struct SlotArmInfo {
SlotArmInfo (Trigger& s);
~SlotArmInfo();
Trigger& slot;
Temporal::timepos_t start;
Temporal::timepos_t end;
std::shared_ptr<RTMidiBuffer> midi_buf; /* assumed large enough */
std::vector<Sample*> audio_buf; /* assumed large enough */
};
} // namespace ARDOUR

View File

@ -82,6 +82,7 @@ LIBARDOUR_API std::string cue_marker_name (int32_t);
class Trigger;
typedef std::shared_ptr<Trigger> TriggerPtr;
typedef RTMidiBufferBase<Temporal::Beats,Temporal::Beats> RTMidiBufferBeats;
class LIBARDOUR_API Trigger : public PBD::Stateful {
public:
@ -294,9 +295,11 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
timepos_t current_pos() const;
double position_as_fraction() const;
virtual void captured (SlotArmInfo&) {}
virtual void arm() {}
virtual void disarm () {}
virtual void captured (SlotArmInfo&, BufferSet&, samplepos_t capture_start) {}
virtual void arm();
virtual void disarm ();
bool armed() const { return _armed; }
PBD::Signal0<void> ArmChanged;
Temporal::BBT_Argument compute_start (Temporal::TempoMap::SharedPtr const &, samplepos_t start, samplepos_t end, Temporal::BBT_Offset const & q, samplepos_t& start_samples, bool& will_start);
virtual timepos_t compute_end (Temporal::TempoMap::SharedPtr const &, Temporal::BBT_Time const &, samplepos_t, Temporal::Beats &) = 0;
@ -324,6 +327,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
void set_region (std::shared_ptr<Region>, bool use_thread = true);
void clear_region ();
virtual int set_region_in_worker_thread (std::shared_ptr<Region>) = 0;
virtual int set_region_in_worker_thread_from_capture (std::shared_ptr<Region>) = 0;
std::shared_ptr<Region> region() const { return _region; }
uint32_t index() const { return _index; }
@ -450,6 +454,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
gain_t _pending_velocity_gain;
gain_t _velocity_gain;
bool _cue_launched;
bool _armed;
/* these are only used by midi triggers but the ui_state API needs them */
Evoral::SMF::UsedChannels _used_channels;
@ -525,6 +530,7 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
bool probably_oneshot () const;
int set_region_in_worker_thread (std::shared_ptr<Region>);
int set_region_in_worker_thread_from_capture (std::shared_ptr<Region>) { return 0; }
void jump_start ();
void jump_stop (BufferSet& bufs, pframes_t dest_offset);
@ -579,7 +585,7 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
MIDITrigger (uint32_t index, TriggerBox&);
~MIDITrigger ();
void captured (SlotArmInfo&);
void captured (SlotArmInfo&, BufferSet&, samplepos_t capture_start);
void arm();
void disarm ();
@ -605,6 +611,7 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
void estimate_midi_patches ();
int set_region_in_worker_thread (std::shared_ptr<Region>);
int set_region_in_worker_thread_from_capture (std::shared_ptr<Region>);
void jump_start ();
void shutdown (BufferSet& bufs, pframes_t dest_offset);
void jump_stop (BufferSet& bufs, pframes_t dest_offset);
@ -638,6 +645,7 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
std::vector<int> const & channel_map() const { return _channel_map; }
void check_edit_swap (timepos_t const &, bool playing, BufferSet&);
RTMidiBufferBeats const & rt_midi_buffer() const { return *rt_midibuffer.load(); }
protected:
void retrigger ();
@ -663,8 +671,6 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
uint32_t first_event_index;
uint32_t last_event_index;
typedef RTMidiBufferBase<Temporal::Beats,Temporal::Beats> RTMidiBufferBeats;
std::atomic<RTMidiBufferBeats*> rt_midibuffer;
std::atomic<RTMidiBufferBeats*> pending_rt_midibuffer;
std::atomic<RTMidiBufferBeats*> old_rt_midibuffer;
@ -687,6 +693,7 @@ class LIBARDOUR_API TriggerBoxThread
void set_region (TriggerBox&, uint32_t slot, std::shared_ptr<Region>);
void request_delete_trigger (Trigger* t);
void request_build_source (Trigger* t);
void summon();
void stop();
@ -699,7 +706,8 @@ class LIBARDOUR_API TriggerBoxThread
enum RequestType {
Quit,
SetRegion,
DeleteTrigger
DeleteTrigger,
BuildSourceAndRegion
};
struct Request {
@ -711,7 +719,7 @@ class LIBARDOUR_API TriggerBoxThread
TriggerBox* box;
uint32_t slot;
std::shared_ptr<Region> region;
/* for DeleteTrigger */
/* for DeleteTrigger and BuildSourceAndRegion */
Trigger* trigger;
void* operator new (size_t);
@ -727,6 +735,9 @@ class LIBARDOUR_API TriggerBoxThread
CrossThreadChannel _xthread;
void queue_request (Request*);
void delete_trigger (Trigger*);
void build_source (Trigger*);
void build_midi_source (MIDITrigger*);
void build_audio_source (AudioTrigger*);
};
struct CueRecord {
@ -741,6 +752,18 @@ struct CueRecord {
typedef PBD::RingBuffer<CueRecord> CueRecords;
struct SlotArmInfo {
SlotArmInfo (Trigger& s);
~SlotArmInfo();
Trigger& slot;
Temporal::timepos_t start;
Temporal::timepos_t end;
RTMidiBuffer* midi_buf; /* assumed large enough */
RTMidiBufferBeats* beats; /* will take over data allocated for midi_but */
std::vector<Sample*> audio_buf; /* assumed large enough */
};
class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_from_this<TriggerBox>
{
public:
@ -763,6 +786,11 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro
bool record_enabled() const { return _record_enabled; }
PBD::Signal0<void> RecEnableChanged;
void arm_from_another_thread (Trigger& slot, samplepos_t, timecnt_t const & expected_duration, uint32_t chans);
void disarm();
bool armed() const { return (bool) _arm_info.load(); }
PBD::Signal0<void> ArmedChanged;
void run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes, bool result_required);
void run_cycle (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes);
bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
@ -912,9 +940,14 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro
bool _cancel_locate_armed;
bool _fast_forwarding;
bool _record_enabled;
samplepos_t capture_start;
PBD::PCGRand _pcg;
void maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes);
void finish_recording (BufferSet& bufs);
void set_armed (SlotArmInfo*);
/* These four are accessed (read/write) only from process() context */
void drop_triggers ();
@ -969,6 +1002,9 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro
int handle_stopped_trigger (BufferSet& bufs, pframes_t dest_offset);
PBD::ScopedConnection stop_all_connection;
std::atomic<SlotArmInfo*> _arm_info;
static TriggerBox* currently_recording;
typedef std::map<std::vector<uint8_t>,std::pair<int,int> > CustomMidiMap;
static CustomMidiMap _custom_midi_map;
@ -1042,5 +1078,3 @@ DEFINE_ENUM_CONVERT(ARDOUR::FollowAction::Type);
DEFINE_ENUM_CONVERT(ARDOUR::Trigger::LaunchStyle);
DEFINE_ENUM_CONVERT(ARDOUR::Trigger::StretchMode);
} /* namespace PBD */

View File

@ -59,7 +59,6 @@
#include "ardour/buffer.h"
#include "ardour/buffer_set.h"
#include "ardour/capturing_processor.h"
#include "ardour/cliprec.h"
#include "ardour/debug.h"
#include "ardour/delivery.h"
#include "ardour/disk_reader.h"
@ -2804,9 +2803,6 @@ Route::set_state (const XMLNode& node, int version)
DiskIOPoint diop;
if (node.get_property (X_("disk-io-point"), diop)) {
if (_clip_recorder) {
_clip_recorder->set_display_to_user (diop == DiskIOCustom);
}
if (_disk_writer) {
_disk_writer->set_display_to_user (diop == DiskIOCustom);
}
@ -3237,9 +3233,6 @@ Route::set_processor_state (const XMLNode& node, int version)
} else if (prop->value() == "diskwriter" && _disk_writer) {
_disk_writer->set_state (**niter, version);
new_order.push_back (_disk_writer);
} else if (prop->value() == "cliprec" && _clip_recorder) {
_clip_recorder->set_state (**niter, version);
new_order.push_back (_clip_recorder);
} else if (prop->value() == "triggerbox") {
if (!_triggerbox) {
_triggerbox.reset (new TriggerBox (_session, _default_type));
@ -5428,7 +5421,7 @@ Route::setup_invisible_processors ()
/* DISK READER & WRITER (for Track objects) */
if (_disk_reader || _disk_writer || _clip_recorder) {
if (_disk_reader || _disk_writer) {
switch (_disk_io_point) {
case DiskIOPreFader:
if (trim != new_processors.end()) {
@ -5476,9 +5469,6 @@ Route::setup_invisible_processors ()
assert (writer_pos == find (new_processors.begin(), new_processors.end(), _disk_writer));
reader_pos = new_processors.insert (++writer_pos, _disk_reader);
}
if (_clip_recorder) {
new_processors.insert (reader_pos, _clip_recorder);
}
}
/* Polarity Invert */
@ -6098,10 +6088,6 @@ Route::set_disk_io_point (DiskIOPoint diop)
display = false;
}
if (_clip_recorder) {
_clip_recorder->set_display_to_user (display);
}
if (_disk_writer) {
_disk_writer->set_display_to_user (display);
}

View File

@ -534,11 +534,9 @@ RTMidiBufferBase<TimeType,DistanceType>::track_state (TimeType when, MidiStateTr
}
template<class TimeType, class DistanceType>
RTMidiBufferBase<Temporal::Beats,Temporal::Beats>*
RTMidiBufferBase<TimeType,DistanceType>::convert()
void
RTMidiBufferBase<TimeType,DistanceType>::convert (RTMidiBufferBase<Temporal::Beats,Temporal::Beats>& beats)
{
RTMidiBufferBase<Temporal::Beats,Temporal::Beats>* beats = new RTMidiBufferBase<Temporal::Beats,Temporal::Beats>();
/* Convert timestamps, taking advantage of the fact that beats and
* samples are both 64 bit integers, and thus Item::timestamp is the
* same size and type for both.
@ -552,19 +550,19 @@ RTMidiBufferBase<TimeType,DistanceType>::convert()
/* Hand over all the data */
beats->_data = reinterpret_cast<RTMidiBufferBase<Temporal::Beats,Temporal::Beats>::Item*> (_data);
beats->_size = _size;
beats->_pool = _pool;
beats->_pool_size = _pool_size;
beats->_pool_size = _pool_size;
beats._data = reinterpret_cast<RTMidiBufferBase<Temporal::Beats,Temporal::Beats>::Item*> (_data);
beats._size = _size;
beats._capacity = _capacity;
beats._reversed = _reversed;
beats._pool = _pool;
beats._pool_size = _pool_size;
beats._pool_size = _pool_size;
_data = nullptr;
_pool = nullptr;
_size = 0;
_pool_size = 0;
_pool_capacity = 0;
return beats;
}

View File

@ -28,7 +28,6 @@
#include "ardour/audiofilesource.h"
#include "ardour/audioplaylist.h"
#include "ardour/audioregion.h"
#include "ardour/cliprec.h"
#include "ardour/debug.h"
#include "ardour/delivery.h"
#include "ardour/disk_reader.h"
@ -90,10 +89,6 @@ Track::~Track ()
if (_disk_writer) {
_disk_writer.reset ();
}
if (_clip_recorder) {
_clip_recorder.reset ();
}
}
int
@ -118,9 +113,6 @@ Track::init ()
_disk_writer->set_block_size (_session.get_block_size ());
_disk_writer->set_owner (this);
_clip_recorder.reset (new ClipRecProcessor (_session, *this, name(), data_type(), *this));
_clip_recorder->set_owner (this);
/* no triggerbox for the auditioner, to avoid visual clutter in
* patchbays and elsewhere (or special-case code in those places)
*/
@ -1000,12 +992,6 @@ Track::set_processor_state (XMLNode const& node, int version, XMLProperty const*
new_order.push_back (_disk_writer);
return true;
}
} else if (prop->value() == "cliprec") {
if (_clip_recorder) {
_clip_recorder->set_state (node, version);
new_order.push_back (_clip_recorder);
return true;
}
}
error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;

View File

@ -44,10 +44,12 @@
#include "ardour/debug.h"
#include "ardour/import_status.h"
#include "ardour/midi_buffer.h"
#include "ardour/midi_channel_filter.h"
#include "ardour/midi_cursor.h"
#include "ardour/midi_model.h"
#include "ardour/midi_state_tracker.h"
#include "ardour/midi_region.h"
#include "ardour/midi_track.h"
#include "ardour/minibpm.h"
#include "ardour/port.h"
#include "ardour/region_factory.h"
@ -238,6 +240,7 @@ Trigger::Trigger (uint32_t n, TriggerBox& b)
, _pending_velocity_gain (1.0)
, _velocity_gain (1.0)
, _cue_launched (false)
, _armed (false)
, _used_channels (Evoral::SMF::UsedChannels())
, _estimated_tempo (0.)
, _segment_tempo (0.)
@ -280,6 +283,24 @@ Trigger::request_trigger_delete (Trigger* t)
TriggerBox::worker->request_delete_trigger (t);
}
void
Trigger::arm ()
{
std::cerr << "T::arm\n";
_box.arm_from_another_thread (*this, _box.session().transport_sample(), timecnt_t::max (Temporal::AudioTime), 0);
_armed = true;
ArmChanged(); /* EMIT SIGNAL */
}
void
Trigger::disarm ()
{
std::cerr << "T::disarm\n";
_box.disarm ();
_armed = false;
ArmChanged(); /* EMIT SIGNAL */
}
void
Trigger::get_ui_state (Trigger::UIState &state) const
{
@ -2182,6 +2203,8 @@ MIDITrigger::MIDITrigger (uint32_t n, TriggerBox& b)
, last_event_samples (0)
, _start_offset (0, 0, 0)
, _legato_offset (0, 0, 0)
, first_event_index (0)
, last_event_index (0)
, rt_midibuffer (nullptr)
, pending_rt_midibuffer (nullptr)
, old_rt_midibuffer (nullptr)
@ -2261,20 +2284,56 @@ MIDITrigger::check_edit_swap (timepos_t const & time, bool playing, BufferSet& b
void
MIDITrigger::arm ()
{
Trigger::arm ();
}
void
MIDITrigger::disarm ()
{
Trigger::disarm ();
}
void
MIDITrigger::captured (SlotArmInfo& ai)
MIDITrigger::captured (SlotArmInfo& ai, BufferSet& bufs, samplepos_t capture_start)
{
RTMidiBufferBeats* rtmb = ai.midi_buf->convert ();
if (ai.midi_buf->size() == 0) {
_armed = false;
ArmChanged(); /* EMIT SIGNAL */
delete &ai;
return;
}
ai.midi_buf->shift (-capture_start);
ai.midi_buf->convert (*ai.beats);
/* Note: the original MIDI buffer in ai is now invalid, all data has
* been moved to rtmb.
*/
old_rt_midibuffer = rt_midibuffer.exchange (ai.beats);
loop_start = Temporal::Beats();
loop_end = (*ai.beats)[ai.beats->size()-1].timestamp;
data_length = loop_end;
_follow_length = Temporal::BBT_Offset (0, data_length.get_beats(), 0);
set_length (timecnt_t (data_length));
iter = 0;
first_event_index = 0;
last_event_index = ai.beats->size();
_follow_action0 = FollowAction::Again;
/* Mark ai.beats as null so that it is not deleted */
ai.beats = nullptr;
delete &ai;
/* start playing */
_box.queue_explict (index());
/* Meanwhile, build a new source and region from the data now in rt_midibuffer */
TriggerBox::worker->request_build_source (this);
_armed = false;
ArmChanged(); /* EMIT SIGNAL */
}
void
@ -2744,6 +2803,36 @@ MIDITrigger::estimate_midi_patches ()
}
int
MIDITrigger::set_region_in_worker_thread_from_capture (std::shared_ptr<Region> r)
{
assert (r);
std::shared_ptr<MidiRegion> mr = std::dynamic_pointer_cast<MidiRegion> (r);
if (!mr) {
return -1;
}
set_region_internal (r);
set_name (mr->name());
_model = mr->model();
_model->ContentsChanged.connect_same_thread (content_connection, boost::bind (&MIDITrigger::model_contents_changed, this));
/* we've changed some of our internal values; we need to update our queued UIState or they will be lost when UIState is applied */
copy_to_ui_state ();
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 loaded midi region, span is %2\n", name(), data_length));
/* This is being used as a kind of shorthand for "everything" which is
pretty stupid
*/
send_property_change (ARDOUR::Properties::name);
return 0;
}
int
MIDITrigger::set_region_in_worker_thread (std::shared_ptr<Region> r)
{
@ -2895,13 +2984,10 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
MidiBuffer* mb (in_process_context? &bufs.get_midi (0) : nullptr);
typedef Evoral::Event<MidiModel::TimeType> MidiEvent;
const timepos_t region_start_time = _region->start();
const timepos_t region_start_time = _region ? _region->start() : timepos_t::zero (Temporal::BeatTime);
const Temporal::Beats region_start = region_start_time.beats();
Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use());
std::shared_ptr<MidiRegion> mr = std::dynamic_pointer_cast<MidiRegion> (_region);
assert (mr);
last_event_samples = end_sample;
/* see if we're going to start or stop or retrigger in this run() call */
@ -2929,7 +3015,7 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
while (iter < rtmb->size() && !_playout) {
RTMidiBufferBeats::Item const & item ((*rtmb)[iter]);
std::cerr << "Looking at event #" << iter << " @ " << item.timestamp << " transition was " << transition_beats << " rs " << region_start << std::endl;
/* Event times are in beats, relative to start of source
* file. We need to conv+ert to region-relative time, and then
* a session timeline time, which is defined by the time at
@ -2941,20 +3027,24 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
/* check that the event is within the bounds for this run() call */
if (maybe_last_event_timeline_beats < start_beats) {
std::cerr << "out1\n";
break;
}
if (iter >= last_event_index) {
iter = rtmb->size();
std::cerr << "out2\n";
break;
}
if (maybe_last_event_timeline_beats > final_beat) {
iter = rtmb->size();
std::cerr << "out3\n";
break;
}
if (maybe_last_event_timeline_beats >= end_beats) {
std::cerr << "out4\n";
break;
}
@ -2962,6 +3052,8 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
const samplepos_t timeline_samples = tmap->sample_at (maybe_last_event_timeline_beats);
std::cerr << "Plays at " << timeline_samples << std::endl;
uint32_t evsize;
uint8_t const * buf = rtmb->bytes (item, evsize);
@ -3205,6 +3297,25 @@ Trigger::make_property_quarks ()
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for queued = %1\n", Properties::queued.property_id));
}
SlotArmInfo::SlotArmInfo (Trigger& s)
: slot (s)
, start (0)
, end (0)
, midi_buf (nullptr)
, beats (nullptr)
{
}
SlotArmInfo::~SlotArmInfo()
{
delete midi_buf;
delete beats;
for (auto & ab : audio_buf) {
delete ab;
}
}
Temporal::BBT_Offset TriggerBox::_assumed_trigger_duration (4, 0, 0);
TriggerBox::TriggerMidiMapMode TriggerBox::_midi_map_mode (TriggerBox::Custom);
int TriggerBox::_first_midi_note = 60;
@ -3214,6 +3325,7 @@ CueRecords TriggerBox::cue_records (256);
std::atomic<bool> TriggerBox::_cue_recording (false);
PBD::Signal0<void> TriggerBox::CueRecordingChanged;
bool TriggerBox::roll_requested = false;
TriggerBox* TriggerBox::currently_recording (nullptr);
bool TriggerBox::_learning = false;
TriggerBox::CustomMidiMap TriggerBox::_custom_midi_map;
std::pair<int,int> TriggerBox::learning_for;
@ -3271,7 +3383,9 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
, _cancel_locate_armed (false)
, _fast_forwarding (false)
, _record_enabled (false)
, capture_start (0)
, requests (1024)
, _arm_info (nullptr)
{
set_display_to_user (false);
@ -3295,6 +3409,142 @@ TriggerBox::TriggerBox (Session& s, DataType dt)
_session.config.ParameterChanged.connect_same_thread (*this, boost::bind (&TriggerBox::parameter_changed, this, _1));
}
void
TriggerBox::arm_from_another_thread (Trigger& slot, samplepos_t now, timecnt_t const & expected_duration, uint32_t chans)
{
using namespace Temporal;
SlotArmInfo* ai = new SlotArmInfo (slot);
if (_data_type == DataType::MIDI) {
ai->midi_buf = new RTMidiBuffer;
ai->midi_buf->resize (1024); // XXX Config->max_slot_midi_event_size
ai->beats = new RTMidiBufferBeats;
} else {
for (uint32_t n = 0; n < chans; ++n) {
ai->audio_buf.push_back (new Sample[_session.sample_rate() * 30]); // XXX Config->max_slot_audio_duration
}
}
Beats start_b;
Beats end_b;
BBT_Argument t_bbt;
Beats t_beats;
samplepos_t t_samples;
TempoMap::SharedPtr tmap (TempoMap::use());
Beats now_beats = tmap->quarters_at (timepos_t (now));
slot.compute_quantized_transition (now, now_beats, std::numeric_limits<Beats>::max(),
t_bbt, t_beats, t_samples, tmap, slot.quantization());
ai->start = t_samples;
ai->end = tmap->sample_at (now_beats + Beats (16, 0)); // XXX slot duration/length
if (currently_recording) {
currently_recording->disarm ();
currently_recording = nullptr;
}
currently_recording = this;
_arm_info = ai;
}
void
TriggerBox::disarm ()
{
std::cerr << "TB::disarm\n";
}
void
TriggerBox::finish_recording (BufferSet& bufs)
{
SlotArmInfo* ai = _arm_info.load();
assert (ai);
ai->slot.captured (*ai, bufs, capture_start);
_arm_info = nullptr;
currently_recording = nullptr;
capture_start = 0;
}
void
TriggerBox::maybe_capture (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes)
{
const size_t n_buffers = bufs.count().n_audio();
SlotArmInfo* ai = _arm_info.load();
if (!ai) {
return;
}
if (!ai->slot.armed() && (currently_recording == this)) {
finish_recording (bufs);
return;
}
if (speed <= 0.) {
std::cerr << "not moving\n";
return;
}
if (start_sample < ai->start.samples()) {
std::cerr << "waiting to capture\n";
return;
}
capture_start = ai->start.samples();
std::cerr << "capturing from " << capture_start << std::endl;
/* Audio */
if (n_buffers) {
/* AUDIO */
for (size_t n = 0; n < n_buffers; ++n) {
assert (ai->audio_buf.size() >= n);
AudioBuffer& buf (bufs.get_audio (n%n_buffers));
memcpy (buf.data(), ai->audio_buf[n], sizeof (Sample) * nframes);
}
}
/* MIDI */
MidiBuffer& buf = bufs.get_midi (0);
Track* trk = static_cast<Track*> (_owner);
MidiTrack* mt = dynamic_cast<MidiTrack*>(trk);
MidiChannelFilter* filter = mt ? &mt->capture_filter() : 0;
if (buf.size()) {
std::cerr << "GOT " << buf.size() << "MIDI events\n";
}
for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
Evoral::Event<MidiBuffer::TimeType> ev (*i, false);
if (ev.time() > nframes) {
break;
}
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 && (!filter || !filter->filter(ev.buffer(), ev.size()))) {
const samplepos_t event_time = start_sample + ev.time();
std::cerr << "\twritten to midi rt buf\n";
ai->midi_buf->write (event_time, ev.event_type(), ev.size(), ev.buffer());
}
}
}
void
TriggerBox::set_record_enabled (bool yn)
{
@ -4338,6 +4588,8 @@ TriggerBox::run_cycle (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
}
#endif
maybe_capture (bufs, start_sample, end_sample, speed, nframes);
bool allstop = _requests.stop_all.exchange (false);
bool was_recorded;
int32_t cue_bang = _session.first_cue_within (start_sample, end_sample, was_recorded);
@ -5096,6 +5348,9 @@ TriggerBoxThread::thread_work ()
case DeleteTrigger:
delete_trigger (req->trigger);
break;
case BuildSourceAndRegion:
build_source (req->trigger);
break;
default:
break;
}
@ -5163,9 +5418,84 @@ TriggerBoxThread::request_delete_trigger (Trigger* t)
queue_request (req);
}
void
TriggerBoxThread::request_build_source (Trigger* t)
{
TriggerBoxThread::Request* req = new TriggerBoxThread::Request (BuildSourceAndRegion);
req->trigger = t;
queue_request (req);
}
void
TriggerBoxThread::delete_trigger (Trigger* t)
{
delete t;
}
void
TriggerBoxThread::build_source (Trigger* t)
{
std::cerr << "Build source!\n";
MIDITrigger* mt = dynamic_cast<MIDITrigger*> (t);
if (mt) {
build_midi_source (mt);
}
}
void
TriggerBoxThread::build_midi_source (MIDITrigger* t)
{
Track* trk = static_cast<Track*> (t->box().owner());
std::shared_ptr<MidiSource> ms = t->box().session().create_midi_source_for_session (trk->name());
assert (ms);
std::cerr << "Created new source from rtmb, called " << ms->name() << std::endl;
RTMidiBufferBeats const & rtmb = t->rt_midi_buffer();
{
MidiSource::WriterLock lock (ms->mutex());
ms->mark_streaming_midi_write_started (lock, Sustained);
for (size_t n = 0; n < rtmb.size(); ++n) {
RTMidiBufferBeats::Item const & item (rtmb[n]);
uint32_t sz;
uint8_t const * const b = rtmb.bytes (item, sz);
Evoral::Event<Temporal::Beats> ev (Evoral::MIDI_EVENT, item.timestamp, sz, b);
ms->append_event_beats (lock, ev);
}
ms->mark_streaming_write_completed (lock);
std::cerr << "Filled with data, now loading model\n";
ms->load_model (lock);
}
/* now build region */
PropertyList plist;
SourceList sources;
std::shared_ptr<FileSource> fs (std::dynamic_pointer_cast<FileSource> (ms));
assert (fs);
sources.push_back (ms);
std::string region_name = region_name_from_path (fs->path(), false, false);
plist.add (ARDOUR::Properties::start, timecnt_t (Temporal::BeatTime));
plist.add (ARDOUR::Properties::length, ms->length ());
plist.add (ARDOUR::Properties::name, region_name);
plist.add (ARDOUR::Properties::layer, 0);
plist.add (ARDOUR::Properties::whole_file, true);
plist.add (ARDOUR::Properties::external, false);
plist.add (ARDOUR::Properties::opaque, true);
std::shared_ptr<Region> whole = RegionFactory::create (sources, plist);
std::cerr << "Created WF region " << whole->name() << std::endl;
/* ... and insert a discrete copy into the playlist*/
PropertyList plist2;
plist2.add (ARDOUR::Properties::whole_file, false);
std::shared_ptr<Region> copy (RegionFactory::create (whole, plist2));
std::cerr << "Created copy region " << whole->name() << std::endl;
t->set_region_in_worker_thread_from_capture (copy);
}

View File

@ -49,7 +49,6 @@ libardour_sources = [
'chan_mapping.cc',
'circular_buffer.cc',
'clip_library.cc',
'cliprec.cc',
'config_text.cc',
'control_group.cc',
'control_protocol_manager.cc',