first functioning MIDI clip recording (libs)
This commit is contained in:
parent
a2d44ba97d
commit
ac4bb55f13
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user