mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 06:44:57 +01:00
Compare commits
No commits in common. "5ef94b05111df203692b463406e773ea4a1f8421" and "efe08f780917aace650d5478c69c13e0500dca66" have entirely different histories.
5ef94b0511
...
efe08f7809
9 changed files with 294 additions and 359 deletions
|
|
@ -430,11 +430,11 @@ AudioClipEditor::position_lines ()
|
|||
return;
|
||||
}
|
||||
|
||||
double start_x1 = sample_to_pixel (_region->start().samples());
|
||||
double end_x0 = start_x1 + sample_to_pixel (_region->length().samples());
|
||||
double width = sample_to_pixel (_region->start().samples());
|
||||
start_line->set (ArdourCanvas::Rect (0., 0., width, _visible_canvas_height));
|
||||
|
||||
start_line->set (ArdourCanvas::Rect (0., 0., start_x1, _visible_canvas_height));
|
||||
end_line->set_position (ArdourCanvas::Duple (end_x0, 0.));
|
||||
double offset = sample_to_pixel ((_region->start() + _region->length()).samples());
|
||||
end_line->set_position (ArdourCanvas::Duple (offset, 0.));
|
||||
end_line->set (ArdourCanvas::Rect (0., 0., ArdourCanvas::COORD_MAX, _visible_canvas_height));
|
||||
}
|
||||
|
||||
|
|
@ -789,23 +789,15 @@ AudioClipEditor::maybe_update ()
|
|||
|
||||
if (playing_trigger->active ()) {
|
||||
if (playing_trigger->the_region()) {
|
||||
|
||||
/* We can't know the precise sample
|
||||
* position because we may be
|
||||
* stretching. So figure out
|
||||
*/
|
||||
std::shared_ptr<ARDOUR::AudioTrigger> at (std::dynamic_pointer_cast<AudioTrigger> (playing_trigger));
|
||||
if (at) {
|
||||
const double f = playing_trigger->position_as_fraction ();
|
||||
_playhead_cursor->set_position (playing_trigger->the_region()->start().samples() + (f * at->data_length()));
|
||||
}
|
||||
_playhead_cursor->set_position (playing_trigger->current_pos().samples() + playing_trigger->the_region()->start().samples());
|
||||
}
|
||||
} else {
|
||||
_playhead_cursor->set_position (0);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (_region) {
|
||||
#if 0
|
||||
} else if (view->midi_region()) {
|
||||
|
||||
/* Timeline region editor */
|
||||
|
||||
|
|
@ -814,13 +806,13 @@ AudioClipEditor::maybe_update ()
|
|||
}
|
||||
|
||||
samplepos_t pos = _session->transport_sample();
|
||||
samplepos_t spos = _region->source_position().samples();
|
||||
samplepos_t spos = view->midi_region()->source_position().samples();
|
||||
if (pos < spos) {
|
||||
_playhead_cursor->set_position (0);
|
||||
} else {
|
||||
_playhead_cursor->set_position (pos - spos);
|
||||
}
|
||||
|
||||
#endif
|
||||
} else {
|
||||
_playhead_cursor->set_position (0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "widgets/tooltips.h"
|
||||
|
||||
#include "ardour/audioregion.h"
|
||||
#include "ardour/location.h"
|
||||
#include "ardour/profile.h"
|
||||
#include "ardour/session.h"
|
||||
|
|
@ -231,14 +230,8 @@ AudioTriggerPropertiesBox::on_trigger_changed (const PBD::PropertyChange& pc)
|
|||
_start_clock.set_mode (mode);
|
||||
_length_clock.set_mode (mode);
|
||||
|
||||
std::shared_ptr<AudioRegion> ar (std::dynamic_pointer_cast<AudioRegion> (at->the_region()));
|
||||
if (ar) {
|
||||
_start_clock.set (ar->start());
|
||||
_length_clock.set (timepos_t (ar->length()));
|
||||
} else {
|
||||
_start_clock.set (timepos_t (0));
|
||||
_length_clock.set (timepos_t (0));
|
||||
}
|
||||
_start_clock.set (at->start_offset ());
|
||||
_length_clock.set (at->current_length ()); // set_duration() ?
|
||||
}
|
||||
|
||||
if ( pc.contains (Properties::tempo_meter) || pc.contains (Properties::follow_length)) {
|
||||
|
|
@ -312,11 +305,11 @@ AudioTriggerPropertiesBox::beats_changed ()
|
|||
void
|
||||
AudioTriggerPropertiesBox::start_clock_changed ()
|
||||
{
|
||||
/* XXX do something, probably to the region */
|
||||
trigger()->set_start(_start_clock.last_when());
|
||||
}
|
||||
|
||||
void
|
||||
AudioTriggerPropertiesBox::length_clock_changed ()
|
||||
{
|
||||
/* XXX do something, probably to the region */
|
||||
trigger()->set_length(_length_clock.current_duration()); //?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -425,24 +425,12 @@ StripImportDialog::refill_import_table ()
|
|||
_strip_table.attach (*manage (new ArdourHSpacer (1.0)), 0, 4, 1, 2, EXPAND | FILL, SHRINK, 4, 8);
|
||||
/* clang-format on */
|
||||
|
||||
std::vector<std::pair<PBD::ID, PBD::ID>> sorted_map;
|
||||
for (auto const& i : _import_map) {
|
||||
sorted_map.push_back (i);
|
||||
}
|
||||
std::sort (sorted_map.begin (), sorted_map.end (), [=] (auto& a, auto& b) {
|
||||
try {
|
||||
return _route_map.at (a.first).pi.order () < _route_map.at (b.first).pi.order ();
|
||||
} catch (...) {
|
||||
}
|
||||
return a.second < b.second;
|
||||
});
|
||||
|
||||
/* Refill table */
|
||||
int r = 1;
|
||||
for (auto& [rid, eid] : sorted_map /*_import_map*/) {
|
||||
for (auto& [rid, eid] : _import_map) {
|
||||
++r;
|
||||
if (_route_map.find (rid) != _route_map.end ()) {
|
||||
l = manage (new Label (_route_map.at (rid).name, 0, 0.5));
|
||||
l = manage (new Label (_route_map[rid], 0, 0.5));
|
||||
} else {
|
||||
l = manage (new Label (_("<i>New Track</i>"), 0, 0.5));
|
||||
l->set_use_markup ();
|
||||
|
|
@ -454,10 +442,10 @@ StripImportDialog::refill_import_table ()
|
|||
#else
|
||||
using namespace Menu_Helpers;
|
||||
ArdourDropdown* dd = manage (new ArdourDropdown ());
|
||||
for (auto& [eid, einfo] : _extern_map) {
|
||||
dd->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (einfo.name), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::change_mapping), dd, rid, eid, einfo.name)));
|
||||
for (auto& [eid, ename] : _extern_map) {
|
||||
dd->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (ename), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::change_mapping), dd, rid, eid, ename)));
|
||||
}
|
||||
dd->set_text (_extern_map.at (eid).name);
|
||||
dd->set_text (_extern_map[eid]);
|
||||
_strip_table.attach (*dd, 2, 3, r, r + 1, EXPAND | FILL, SHRINK);
|
||||
#endif
|
||||
|
||||
|
|
@ -497,18 +485,18 @@ StripImportDialog::refill_import_table ()
|
|||
_add_rid_dropdown->add_menu_elem (MenuElem (_(" -- New Track -- "), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, next_new, _("New Track"))));
|
||||
sizing_texts.push_back (_(" -- New Track -- "));
|
||||
|
||||
for (auto& [rid, rinfo] : _route_map) {
|
||||
for (auto& [rid, rname] : _route_map) {
|
||||
if (_import_map.find (rid) != _import_map.end ()) {
|
||||
continue;
|
||||
}
|
||||
_add_rid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (rinfo.name), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, rid, rinfo.name)));
|
||||
sizing_texts.push_back (rinfo.name);
|
||||
_add_rid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (rname), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), false, rid, rname)));
|
||||
sizing_texts.push_back (rname);
|
||||
}
|
||||
|
||||
_add_eid_dropdown = manage (new ArdourWidgets::ArdourDropdown ());
|
||||
for (auto& [eid, einfo] : _extern_map) {
|
||||
_add_eid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (einfo.name), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), true, eid, einfo.name)));
|
||||
sizing_texts.push_back (einfo.name);
|
||||
for (auto& [eid, ename] : _extern_map) {
|
||||
_add_eid_dropdown->add_menu_elem (MenuElem (Gtkmm2ext::markup_escape_text (ename), sigc::bind (sigc::mem_fun (*this, &StripImportDialog::prepare_mapping), true, eid, ename)));
|
||||
sizing_texts.push_back (ename);
|
||||
}
|
||||
|
||||
_add_rid_dropdown->set_sizing_texts (sizing_texts);
|
||||
|
|
@ -600,7 +588,7 @@ StripImportDialog::import_all_strips ()
|
|||
_import_map.clear ();
|
||||
|
||||
int64_t next_id = std::numeric_limits<uint64_t>::max () - 1 - _extern_map.size ();
|
||||
for (auto& [eid, einfo] : _extern_map) {
|
||||
for (auto& [eid, ename] : _extern_map) {
|
||||
PBD::ID next_new = PBD::ID (next_id++);
|
||||
_import_map[next_new] = eid;
|
||||
}
|
||||
|
|
@ -616,23 +604,17 @@ StripImportDialog::set_default_mapping (bool and_idle_update)
|
|||
|
||||
if (_match_pbd_id) {
|
||||
/* try a 1:1 mapping */
|
||||
for (auto& [eid, einfo] : _extern_map) {
|
||||
for (auto& [eid, ename] : _extern_map) {
|
||||
if (_route_map.find (eid) != _route_map.end ()) {
|
||||
_import_map[eid] = eid;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* match by name */
|
||||
for (auto& [eid, einfo] : _extern_map) {
|
||||
for (auto& [eid, ename] : _extern_map) {
|
||||
// TODO consider building a reverse [pointer] map
|
||||
for (auto& [rid, rinfo] : _route_map) {
|
||||
#ifdef MIXBUS
|
||||
if (einfo.mixbus > 0 && einfo.mixbus == rinfo.mixbus) {
|
||||
_import_map[rid] = eid;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (einfo == rinfo) {
|
||||
for (auto& [rid, rname] : _route_map) {
|
||||
if (ename == rname) {
|
||||
_import_map[rid] = eid;
|
||||
break;
|
||||
}
|
||||
|
|
@ -650,11 +632,7 @@ StripImportDialog::setup_strip_import_page ()
|
|||
_route_map.clear ();
|
||||
|
||||
for (auto const& r : *_session->get_routes ()) {
|
||||
#ifdef MIXBUS
|
||||
_route_map.emplace (r->id (), Session::RouteImportInfo (r->name (), r->presentation_info (), c->mixbus ()));
|
||||
#else
|
||||
_route_map.emplace (r->id (), Session::RouteImportInfo (r->name (), r->presentation_info (), 0));
|
||||
#endif
|
||||
_route_map[r->id ()] = r->name ();
|
||||
}
|
||||
|
||||
set_default_mapping (false);
|
||||
|
|
|
|||
|
|
@ -28,14 +28,17 @@
|
|||
#include <ytkmm/treestore.h>
|
||||
#include <ytkmm/treeview.h>
|
||||
|
||||
#include "pbd/id.h"
|
||||
|
||||
#include "ardour/search_paths.h"
|
||||
#include "ardour/session.h"
|
||||
#include "pbd/id.h"
|
||||
|
||||
#include "ardour/template_utils.h"
|
||||
#include "ardour_dialog.h"
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
class Session;
|
||||
}
|
||||
|
||||
namespace ArdourWidgets
|
||||
{
|
||||
class ArdourButton;
|
||||
|
|
@ -123,12 +126,11 @@ private:
|
|||
ArdourWidgets::ArdourButton* _reset_mapping;
|
||||
ArdourWidgets::ArdourButton* _import_strips;
|
||||
|
||||
bool _match_pbd_id;
|
||||
std::string _path;
|
||||
std::map<PBD::ID, PBD::ID> _import_map;
|
||||
|
||||
std::map<PBD::ID, ARDOUR::Session::RouteImportInfo> _extern_map;
|
||||
std::map<PBD::ID, ARDOUR::Session::RouteImportInfo> _route_map;
|
||||
bool _match_pbd_id;
|
||||
std::string _path;
|
||||
std::map<PBD::ID, std::string> _extern_map;
|
||||
std::map<PBD::ID, std::string> _route_map;
|
||||
std::map<PBD::ID, PBD::ID> _import_map;
|
||||
|
||||
PBD::ID _add_rid;
|
||||
PBD::ID _add_eid;
|
||||
|
|
|
|||
|
|
@ -664,39 +664,17 @@ public:
|
|||
std::vector<std::string> possible_states() const;
|
||||
static std::vector<std::string> possible_states (std::string path);
|
||||
|
||||
|
||||
enum RouteGroupImportMode {
|
||||
IgnoreRouteGroup,
|
||||
UseRouteGroup,
|
||||
CreateRouteGroup
|
||||
};
|
||||
|
||||
struct RouteImportInfo {
|
||||
RouteImportInfo (std::string const& n, PresentationInfo const& p, int mb)
|
||||
: name (n)
|
||||
, pi (p)
|
||||
, mixbus (mb)
|
||||
{}
|
||||
|
||||
std::string name;
|
||||
PresentationInfo pi;
|
||||
int mixbus;
|
||||
|
||||
bool operator< (RouteImportInfo const& o) {
|
||||
if (mixbus != o.mixbus) {
|
||||
return mixbus < o.mixbus;
|
||||
}
|
||||
return name < o.name;
|
||||
}
|
||||
|
||||
bool operator== (RouteImportInfo const& o) {
|
||||
return mixbus == o.mixbus && name == o.name;
|
||||
}
|
||||
};
|
||||
|
||||
bool export_route_state (std::shared_ptr<RouteList> rl, const std::string& path, bool with_sources);
|
||||
int import_route_state (const std::string& path, std::map<PBD::ID, PBD::ID> const&, RouteGroupImportMode rgim = CreateRouteGroup);
|
||||
|
||||
std::map<PBD::ID, RouteImportInfo> parse_route_state (const std::string& path, bool& match_pbd_id);
|
||||
std::map<PBD::ID, std::string> parse_route_state (const std::string& path, bool& match_pbd_id);
|
||||
|
||||
/// The instant xml file is written to the session directory
|
||||
void add_instant_xml (XMLNode&, bool write_to_config = true);
|
||||
|
|
|
|||
|
|
@ -286,6 +286,10 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
|
|||
virtual pframes_t run (BufferSet&, samplepos_t start_sample, samplepos_t end_sample,
|
||||
Temporal::Beats const & start, Temporal::Beats const & end,
|
||||
pframes_t nframes, pframes_t offset, double bpm, pframes_t& quantize_offset) = 0;
|
||||
virtual void set_start (timepos_t const &) = 0;
|
||||
virtual void set_end (timepos_t const &) = 0;
|
||||
virtual void set_length (timecnt_t const &) = 0;
|
||||
virtual void reload (BufferSet&, void*) = 0;
|
||||
virtual void io_change () {}
|
||||
virtual void set_legato_offset (timepos_t const & offset) = 0;
|
||||
|
||||
|
|
@ -315,6 +319,10 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
|
|||
|
||||
virtual bool probably_oneshot () const = 0;
|
||||
|
||||
virtual timepos_t start_offset () const = 0; /* offset from start of data */
|
||||
virtual timepos_t current_length() const = 0; /* offset from start() */
|
||||
virtual timepos_t natural_length() const = 0; /* offset from start() */
|
||||
|
||||
void process_state_requests (BufferSet& bufs, pframes_t dest_offset);
|
||||
|
||||
bool active() const { return _state >= Running; }
|
||||
|
|
@ -438,7 +446,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
|
|||
|
||||
std::shared_ptr<Region> _region;
|
||||
samplecnt_t process_index;
|
||||
samplepos_t final_process_index; /* where we stop playing, in process time, compare with process_index */
|
||||
samplepos_t final_processed_sample; /* where we stop playing, in process time, compare with process_index */
|
||||
UIState ui_state;
|
||||
TriggerBox& _box;
|
||||
UIRequests _requests;
|
||||
|
|
@ -511,8 +519,7 @@ class LIBARDOUR_API Trigger : public PBD::Stateful {
|
|||
std::atomic<PendingSwap*> pending_swap;
|
||||
std::atomic<PendingSwap*> old_pending_swap;
|
||||
|
||||
virtual int load_pending_data (PendingSwap&) = 0;
|
||||
virtual PendingSwap* pending_factory() const = 0;
|
||||
virtual void adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool from_region) = 0;
|
||||
};
|
||||
|
||||
class LIBARDOUR_API AudioTrigger : public Trigger {
|
||||
|
|
@ -539,8 +546,15 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
|
|||
double segment_beatcnt () { return _beatcnt; }
|
||||
void set_segment_beatcnt (double count);
|
||||
|
||||
void set_start (timepos_t const &);
|
||||
void set_end (timepos_t const &);
|
||||
void set_legato_offset (timepos_t const &);
|
||||
void set_length (timecnt_t const &);
|
||||
void set_user_data_length (samplecnt_t);
|
||||
timepos_t start_offset () const; /* offset from start of data */
|
||||
timepos_t current_length() const; /* offset from start of data */
|
||||
timepos_t natural_length() const; /* offset from start of data */
|
||||
void reload (BufferSet&, void*);
|
||||
void io_change ();
|
||||
bool probably_oneshot () const;
|
||||
|
||||
|
|
@ -571,35 +585,28 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
|
|||
|
||||
AudioData () : length (0), capacity (0) {}
|
||||
~AudioData ();
|
||||
AudioData& operator= (AudioData& other); /* really move semantics */
|
||||
|
||||
samplecnt_t append (Sample const * src, samplecnt_t cnt, uint32_t chan);
|
||||
void alloc (samplecnt_t cnt, uint32_t nchans);
|
||||
void reset () { length = 0; }
|
||||
void drop ();
|
||||
};
|
||||
|
||||
|
||||
Sample const * audio_data (size_t n) const;
|
||||
size_t data_length() const { return data.length; }
|
||||
|
||||
struct AudioPendingSwap : public PendingSwap {
|
||||
AudioData audio_data;
|
||||
|
||||
AudioPendingSwap() {}
|
||||
~AudioPendingSwap() {}
|
||||
};
|
||||
samplecnt_t user_data_length() const { return _user_data_length; }
|
||||
|
||||
void check_edit_swap (timepos_t const &, bool playing, BufferSet&);
|
||||
|
||||
protected:
|
||||
void retrigger ();
|
||||
PendingSwap* pending_factory() const;
|
||||
int load_pending_data (PendingSwap&);
|
||||
void adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool from_region);
|
||||
|
||||
private:
|
||||
AudioData data;
|
||||
AudioData data;
|
||||
samplecnt_t _user_data_length;
|
||||
RubberBand::RubberBandStretcher* _stretcher;
|
||||
samplepos_t _start_offset;
|
||||
|
||||
/* computed during run */
|
||||
|
||||
|
|
@ -613,8 +620,8 @@ class LIBARDOUR_API AudioTrigger : public Trigger {
|
|||
|
||||
virtual void setup_stretcher ();
|
||||
|
||||
void drop_data (AudioData&);
|
||||
int load_data (std::shared_ptr<AudioRegion>, AudioData&);
|
||||
void drop_data ();
|
||||
int load_data (std::shared_ptr<AudioRegion>);
|
||||
void estimate_tempo ();
|
||||
void reset_stretcher ();
|
||||
void _startup (BufferSet&, pframes_t dest_offset, Temporal::BBT_Offset const &);
|
||||
|
|
@ -645,6 +652,9 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
|
|||
void set_length (timecnt_t const &);
|
||||
timepos_t start_offset () const;
|
||||
timepos_t end() const; /* offset from start of data */
|
||||
timepos_t current_length() const; /* offset from start of data */
|
||||
timepos_t natural_length() const; /* offset from start of data */
|
||||
void reload (BufferSet&, void*);
|
||||
bool probably_oneshot () const;
|
||||
|
||||
void tempo_map_changed();
|
||||
|
|
@ -685,8 +695,6 @@ 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&);
|
||||
PendingSwap* pending_factory() const;
|
||||
|
||||
RTMidiBufferBeats const & rt_midi_buffer() const { return *rt_midibuffer.load(); }
|
||||
|
||||
Temporal::Beats play_start() const { return _play_start; }
|
||||
|
|
@ -698,7 +706,6 @@ class LIBARDOUR_API MIDITrigger : public Trigger {
|
|||
void retrigger ();
|
||||
void _arm (Temporal::BBT_Offset const &);
|
||||
void adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool from_region);
|
||||
int load_pending_data (PendingSwap&);
|
||||
|
||||
private:
|
||||
PBD::ID data_source;
|
||||
|
|
@ -919,6 +926,7 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro
|
|||
TriggerPtr get_next_trigger ();
|
||||
TriggerPtr peek_next_trigger ();
|
||||
|
||||
void request_reload (int32_t slot, void*);
|
||||
void set_region (uint32_t slot, std::shared_ptr<Region> region);
|
||||
|
||||
void non_realtime_transport_stop (samplepos_t now, bool flush);
|
||||
|
|
@ -1041,6 +1049,7 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro
|
|||
struct Request {
|
||||
enum Type {
|
||||
Use,
|
||||
Reload,
|
||||
};
|
||||
|
||||
Type type;
|
||||
|
|
@ -1068,6 +1077,8 @@ class LIBARDOUR_API TriggerBox : public Processor, public std::enable_shared_fro
|
|||
void process_requests (BufferSet&);
|
||||
void process_request (BufferSet&, Request*);
|
||||
|
||||
void reload (BufferSet& bufs, int32_t slot, void* ptr);
|
||||
|
||||
void cancel_locate_armed ();
|
||||
void fast_forward_nothing_to_do ();
|
||||
int handle_stopped_trigger (BufferSet& bufs, pframes_t dest_offset);
|
||||
|
|
|
|||
|
|
@ -1197,19 +1197,6 @@ Session::export_route_state (std::shared_ptr<RouteList> rl, const string& path,
|
|||
return tree.write (sn.c_str());
|
||||
}
|
||||
|
||||
static bool
|
||||
allow_import_route_state (PresentationInfo const& pi)
|
||||
{
|
||||
if (pi.special (false)) { // |SurroundMaster|MonitorOut|Auditioner
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pi.flags() & (PresentationInfo::FoldbackBus | PresentationInfo::VBMAny)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
allow_import_route_state (XMLNode const& node, int version)
|
||||
{
|
||||
|
|
@ -1221,13 +1208,20 @@ allow_import_route_state (XMLNode const& node, int version)
|
|||
PresentationInfo pi (PresentationInfo::Flag (0));
|
||||
pi.set_state (*pnode, version);
|
||||
|
||||
return allow_import_route_state (pi);
|
||||
if (pi.special (false)) { // |SurroundMaster|MonitorOut|Auditioner
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pi.flags() & (PresentationInfo::FoldbackBus | PresentationInfo::VBMAny)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<PBD::ID, Session::RouteImportInfo>
|
||||
std::map<PBD::ID, std::string>
|
||||
Session::parse_route_state (const string& path, bool& match_pbd_id)
|
||||
{
|
||||
std::map<PBD::ID, RouteImportInfo> rv;
|
||||
std::map<PBD::ID, std::string> rv;
|
||||
|
||||
XMLTree tree;
|
||||
if (!tree.read (path)) {
|
||||
|
|
@ -1262,24 +1256,11 @@ Session::parse_route_state (const string& path, bool& match_pbd_id)
|
|||
continue;
|
||||
}
|
||||
|
||||
XMLNode* pnode = rxml->child (PresentationInfo::state_node_name.c_str ());
|
||||
if (!pnode) {
|
||||
if (!allow_import_route_state (*rxml, version)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PresentationInfo pi (PresentationInfo::Flag (0));
|
||||
pi.set_state (*pnode, version);
|
||||
|
||||
if (!allow_import_route_state (pi)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int mixbus = 0;
|
||||
#ifdef MIXBUS
|
||||
rxml->get_property (X_("mixbus-num"), mixbus)
|
||||
#endif
|
||||
|
||||
rv.emplace (id, RouteImportInfo (name, pi, mixbus));
|
||||
rv[id] = name;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ Trigger::Trigger (uint32_t n, TriggerBox& b)
|
|||
, _name (Properties::name, "")
|
||||
, _color (Properties::color, 0xBEBEBEFF)
|
||||
, process_index (0)
|
||||
, final_process_index (0)
|
||||
, final_processed_sample (0)
|
||||
, _box (b)
|
||||
, _state (Stopped)
|
||||
, _playout (false)
|
||||
|
|
@ -726,10 +726,6 @@ Trigger::set_region (std::shared_ptr<Region> r, bool use_thread)
|
|||
/* load data, do analysis in another thread */
|
||||
TriggerBox::worker->set_region (_box, index(), r);
|
||||
} else {
|
||||
/* despite the name, this runs in the current thread. The name
|
||||
comes from the fact that this is normally called from a worker
|
||||
thread. It executes in thread it was called in.
|
||||
*/
|
||||
set_region_in_worker_thread (r);
|
||||
}
|
||||
}
|
||||
|
|
@ -773,8 +769,7 @@ Trigger::region_property_change (PropertyChange const & what_changed)
|
|||
void
|
||||
Trigger::bounds_changed (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & len)
|
||||
{
|
||||
PendingSwap* pending = pending_factory();
|
||||
assert (pending);
|
||||
PendingSwap* pending = new PendingSwap;
|
||||
|
||||
pending->play_start = start;
|
||||
pending->play_end = end;
|
||||
|
|
@ -782,8 +777,6 @@ Trigger::bounds_changed (Temporal::timepos_t const & start, Temporal::timepos_t
|
|||
pending->loop_end = pending->play_end;
|
||||
pending->length = len;
|
||||
|
||||
load_pending_data (*pending);
|
||||
|
||||
/* And set it. RT thread will find this and do what needs to be done */
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 pushed pending swap @ %3 for bounds change\n", _box.order(), index(), pending));
|
||||
|
|
@ -811,7 +804,7 @@ Trigger::position_as_fraction () const
|
|||
return 0.0;
|
||||
}
|
||||
|
||||
return process_index / (double) final_process_index;
|
||||
return process_index / (double) final_processed_sample;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1380,37 +1373,6 @@ Trigger::start_and_roll_to (samplepos_t start_pos, samplepos_t end_position, Tri
|
|||
|
||||
/*--------------------*/
|
||||
|
||||
void
|
||||
AudioTrigger::AudioData::drop ()
|
||||
{
|
||||
for (auto& d : *this) {
|
||||
delete [] d;
|
||||
}
|
||||
|
||||
clear ();
|
||||
}
|
||||
|
||||
AudioTrigger::AudioData&
|
||||
AudioTrigger::AudioData::operator= (AudioTrigger::AudioData& other)
|
||||
{
|
||||
/* This is really implementing move semantics between two AudioData objects */
|
||||
|
||||
drop ();
|
||||
|
||||
reserve (other.size());
|
||||
for (auto & sample_ptr : other) {
|
||||
push_back (sample_ptr);
|
||||
}
|
||||
length = other.length;
|
||||
capacity = other.capacity;
|
||||
|
||||
other.clear ();
|
||||
other.length = 0;
|
||||
other.capacity = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
AudioTrigger::AudioData::~AudioData ()
|
||||
{
|
||||
for (auto & s : *this) {
|
||||
|
|
@ -1448,7 +1410,8 @@ AudioTrigger::AudioData::append (Sample const * src, samplecnt_t cnt, uint32_t c
|
|||
|
||||
AudioTrigger::AudioTrigger (uint32_t n, TriggerBox& b)
|
||||
: Trigger (n, b)
|
||||
, _stretcher (nullptr)
|
||||
, _stretcher (0)
|
||||
, _start_offset (0)
|
||||
, read_index (0)
|
||||
, last_readable_sample (0)
|
||||
, _legato_offset (0)
|
||||
|
|
@ -1461,7 +1424,7 @@ AudioTrigger::AudioTrigger (uint32_t n, TriggerBox& b)
|
|||
|
||||
AudioTrigger::~AudioTrigger ()
|
||||
{
|
||||
data.drop ();
|
||||
drop_data ();
|
||||
delete _stretcher;
|
||||
}
|
||||
|
||||
|
|
@ -1583,6 +1546,9 @@ XMLNode&
|
|||
AudioTrigger::get_state () const
|
||||
{
|
||||
XMLNode& node (Trigger::get_state());
|
||||
|
||||
node.set_property (X_("start"), timepos_t (_start_offset));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
|
@ -1595,18 +1561,41 @@ AudioTrigger::set_state (const XMLNode& node, int version)
|
|||
return -1;
|
||||
}
|
||||
|
||||
node.get_property (X_("start"), t);
|
||||
_start_offset = t.samples();
|
||||
|
||||
/* we've changed our internal values; we need to update our queued UIState or they will be lost when UIState is applied */
|
||||
copy_to_ui_state ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
AudioTrigger::set_start (timepos_t const & s)
|
||||
{
|
||||
/* XXX better minimum size needed */
|
||||
_start_offset = std::max (samplepos_t (4096), s.samples ());
|
||||
}
|
||||
|
||||
void
|
||||
AudioTrigger::set_end (timepos_t const & e)
|
||||
{
|
||||
assert (!data.empty());
|
||||
set_length (timecnt_t (e.samples() - _start_offset, timepos_t (_start_offset)));
|
||||
}
|
||||
|
||||
void
|
||||
AudioTrigger::set_legato_offset (timepos_t const & offset)
|
||||
{
|
||||
_legato_offset = offset.samples();
|
||||
}
|
||||
|
||||
timepos_t
|
||||
AudioTrigger::start_offset () const
|
||||
{
|
||||
return timepos_t (_start_offset);
|
||||
}
|
||||
|
||||
void
|
||||
AudioTrigger::start_and_roll_to (samplepos_t start_pos, samplepos_t end_position, uint32_t cnt)
|
||||
{
|
||||
|
|
@ -1620,7 +1609,7 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal:
|
|||
|
||||
expected_end_sample: (TIMELINE!) the sample position where the data for the clip should run out (taking stretch into account)
|
||||
last_readable_sample: (DATA RELATIVE!) the sample in the data where we stop reading
|
||||
final_process_index: (DATA RELATIVE!) the sample where the trigger stops and the follow action if any takes effect
|
||||
final_processed_sample: (DATA RELATIVE!) the sample where the trigger stops and the follow action if any takes effect
|
||||
|
||||
Things that affect these values:
|
||||
|
||||
|
|
@ -1637,37 +1626,51 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal:
|
|||
*/
|
||||
const Temporal::Beats bc (Temporal::Beats::from_double (_beatcnt));
|
||||
|
||||
/* This is tempo-sensitive - we actually compute the sample position bc
|
||||
beats after the transition sample, using either the follow length
|
||||
or _beatcnt
|
||||
*/
|
||||
samplepos_t end_by_follow_length = tmap->sample_at (tmap->bbt_walk (transition_bba, _follow_length));
|
||||
samplepos_t end_by_beatcnt = tmap->sample_at (tmap->bbt_walk (transition_bba, Temporal::BBT_Offset (0, bc.get_beats(), bc.get_ticks())));
|
||||
samplepos_t end_by_user_data_length = transition_sample + (_user_data_length - _start_offset);
|
||||
samplepos_t end_by_data_length = transition_sample + (data.length - _start_offset);
|
||||
samplepos_t end_by_fixed_samples = std::min (end_by_user_data_length, end_by_data_length);
|
||||
|
||||
const Temporal::BBT_Offset beat_length = internal_use_follow_length() ? _follow_length : Temporal::BBT_Offset (0, bc.get_beats(), bc.get_ticks());
|
||||
samplepos_t end_by_beats = tmap->sample_at (tmap->bbt_walk (transition_bba, beat_length));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 computing end with fl %2 bc %3 dl %4 udl %5\n", index(),
|
||||
_follow_length, bc.str(), data.length, _user_data_length));
|
||||
|
||||
/* These are non-tempo-sensitive, and represent data-centric sample counts. */
|
||||
samplepos_t end_by_data_length = transition_sample + data.length;
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 computing end with fl %2 bc %3 dl %4 EDL %5\n", index(),
|
||||
_follow_length, bc.str(), data.length, end_by_data_length));
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 @ %2 / %3 / %4 ends: FL %5 BC %6 from %7 DL %8\n",
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 SO %9 @ %2 / %3 / %4 ends: FL %5 (from %6) BC %7 DL %8\n",
|
||||
index(), transition_sample, transition_beats, transition_bbt,
|
||||
_follow_length, end_by_beats,
|
||||
internal_use_follow_length() ? _follow_length : Temporal::BBT_Offset (0, bc.get_beats(), bc.get_ticks()),
|
||||
end_by_data_length));
|
||||
end_by_follow_length, _follow_length, end_by_beatcnt, end_by_data_length, _start_offset));
|
||||
|
||||
if (stretching()) {
|
||||
expected_end_sample = end_by_beats;
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("stretching, end up at %1\n", expected_end_sample));
|
||||
/* NOTES: beatcnt here will reflect the stretch, which is OK
|
||||
Because we are stretching. But .. that's wrong when we're not
|
||||
stretching. So we really need another value: something like
|
||||
data_length but user-definable.
|
||||
*/
|
||||
if (internal_use_follow_length()) {
|
||||
expected_end_sample = std::min (end_by_follow_length, end_by_beatcnt);
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("stretch and use follow, end up at %1\n", expected_end_sample));
|
||||
} else {
|
||||
expected_end_sample = end_by_beatcnt;
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("stretch and no-follow, end up at %1\n", expected_end_sample));
|
||||
}
|
||||
} else {
|
||||
expected_end_sample = std::min (end_by_beats, end_by_data_length);
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("no-stretch, end up at %1\n", expected_end_sample));
|
||||
if (internal_use_follow_length()) {
|
||||
expected_end_sample = std::min (end_by_follow_length, end_by_fixed_samples);
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("no-stretch and use follow, end up at %1\n", expected_end_sample));
|
||||
} else {
|
||||
expected_end_sample = end_by_fixed_samples;
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("no-stretch and no-follow, end up at %1\n", expected_end_sample));
|
||||
}
|
||||
}
|
||||
|
||||
final_process_index = expected_end_sample - transition_sample;
|
||||
samplecnt_t usable_length = end_by_data_length - transition_samples;
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("usable length from end-by-samples %1 - transition @ %2 = %3\n", end_by_data_length, transition_samples, usable_length));
|
||||
final_processed_sample = expected_end_sample - transition_sample;
|
||||
|
||||
samplecnt_t usable_length;
|
||||
|
||||
if (internal_use_follow_length() && (end_by_follow_length < end_by_data_length)) {
|
||||
usable_length = std::min (end_by_follow_length - transition_samples, end_by_fixed_samples - transition_samples);
|
||||
} else {
|
||||
usable_length = end_by_fixed_samples - transition_samples;
|
||||
}
|
||||
|
||||
/* called from compute_end() when we know the time (audio &
|
||||
* musical time domains when we start starting. Our job here is to
|
||||
|
|
@ -1678,7 +1681,7 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal:
|
|||
|
||||
if (launch_style() != Repeat || (q == Temporal::BBT_Offset())) {
|
||||
|
||||
last_readable_sample = usable_length;
|
||||
last_readable_sample = _start_offset + usable_length;
|
||||
|
||||
} else {
|
||||
|
||||
|
|
@ -1690,14 +1693,14 @@ AudioTrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal:
|
|||
/* XXX MUST HANDLE BAR-LEVEL QUANTIZATION */
|
||||
|
||||
timecnt_t len (Temporal::Beats (q.beats, q.ticks), timepos_t (Temporal::Beats()));
|
||||
last_readable_sample = len.samples();
|
||||
last_readable_sample = _start_offset + len.samples();
|
||||
}
|
||||
|
||||
effective_length = tmap->quarters_at_sample (transition_sample + final_process_index) - tmap->quarters_at_sample (transition_sample);
|
||||
effective_length = tmap->quarters_at_sample (transition_sample + final_processed_sample) - tmap->quarters_at_sample (transition_sample);
|
||||
|
||||
_transition_bbt = transition_bbt;
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1: final process index %2 expected end (timeline) sample %3 final read index %4\n", index(), final_process_index, expected_end_sample, last_readable_sample));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1: final sample %2 vs ees %3 ls %4\n", index(), final_processed_sample, expected_end_sample, last_readable_sample));
|
||||
|
||||
return timepos_t (expected_end_sample);
|
||||
}
|
||||
|
|
@ -1708,6 +1711,30 @@ AudioTrigger::set_length (timecnt_t const & newlen)
|
|||
/* XXX what */
|
||||
}
|
||||
|
||||
void
|
||||
AudioTrigger::set_user_data_length (samplecnt_t s)
|
||||
{
|
||||
_user_data_length = s;
|
||||
send_property_change (ARDOUR::Properties::length);
|
||||
}
|
||||
|
||||
timepos_t
|
||||
AudioTrigger::current_length() const
|
||||
{
|
||||
if (_region) {
|
||||
return timepos_t (data.length);
|
||||
}
|
||||
return timepos_t (Temporal::BeatTime);
|
||||
}
|
||||
|
||||
timepos_t
|
||||
AudioTrigger::natural_length() const
|
||||
{
|
||||
if (_region) {
|
||||
return timepos_t::from_superclock (_region->length().magnitude());
|
||||
}
|
||||
return timepos_t (Temporal::BeatTime);
|
||||
}
|
||||
int
|
||||
AudioTrigger::set_region_in_worker_thread_from_capture (std::shared_ptr<Region> r)
|
||||
{
|
||||
|
|
@ -1744,10 +1771,12 @@ AudioTrigger::set_region_in_worker_thread_internal (std::shared_ptr<Region> r, b
|
|||
}
|
||||
|
||||
if (!from_capture) {
|
||||
load_data (ar, data);
|
||||
load_data (ar);
|
||||
}
|
||||
|
||||
set_name (ar->name());
|
||||
if (from_capture) {
|
||||
set_name (r->name());
|
||||
}
|
||||
|
||||
estimate_tempo (); /* NOTE: if this is an existing clip (D+D copy) then it will likely have a SD tempo, and that short-circuits minibpm for us */
|
||||
|
||||
|
|
@ -1868,6 +1897,15 @@ AudioTrigger::setup_stretcher ()
|
|||
_stretcher->setMaxProcessSize (rb_blocksize);
|
||||
}
|
||||
|
||||
void
|
||||
AudioTrigger::drop_data ()
|
||||
{
|
||||
for (auto& d : data) {
|
||||
delete [] d;
|
||||
}
|
||||
data.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
AudioTrigger::captured (SlotArmInfo& ai)
|
||||
{
|
||||
|
|
@ -1881,6 +1919,7 @@ AudioTrigger::captured (SlotArmInfo& ai)
|
|||
|
||||
data.length = ai.audio_buf.length;
|
||||
data.capacity = ai.audio_buf.capacity;
|
||||
_user_data_length = data.length;
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 captured a total of %3\n", _box.order(), _index, data.length));
|
||||
|
||||
|
|
@ -1921,25 +1960,27 @@ AudioTrigger::captured (SlotArmInfo& ai)
|
|||
}
|
||||
|
||||
int
|
||||
AudioTrigger::load_data (std::shared_ptr<AudioRegion> ar, AudioData& audio_data)
|
||||
AudioTrigger::load_data (std::shared_ptr<AudioRegion> ar)
|
||||
{
|
||||
const uint32_t nchans = ar->n_channels();
|
||||
|
||||
audio_data.drop ();
|
||||
drop_data ();
|
||||
|
||||
try {
|
||||
samplecnt_t len = ar->length_samples();
|
||||
|
||||
audio_data.alloc (len, nchans);
|
||||
data.alloc (len, nchans);
|
||||
|
||||
for (uint32_t n = 0; n < nchans; ++n) {
|
||||
ar->read (audio_data[n], 0, len, n);
|
||||
ar->read (data[n], 0, len, n);
|
||||
}
|
||||
|
||||
audio_data.length = len;
|
||||
data.length = len;
|
||||
_user_data_length = len;
|
||||
set_name (ar->name());
|
||||
|
||||
} catch (...) {
|
||||
audio_data.drop ();
|
||||
drop_data ();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1954,7 +1995,7 @@ AudioTrigger::retrigger ()
|
|||
update_properties ();
|
||||
reset_stretcher ();
|
||||
|
||||
read_index = _legato_offset;
|
||||
read_index = _start_offset + _legato_offset;
|
||||
retrieved = 0;
|
||||
_legato_offset = 0; /* used one time only */
|
||||
|
||||
|
|
@ -2057,26 +2098,15 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
#ifdef HAVE_RUBBERBAND_3_0_0
|
||||
to_pad = _stretcher->getPreferredStartPad();
|
||||
to_drop = _stretcher->getStartDelay();
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 requires padding of %2 dropping %3 (RB v3\n", name(), to_pad, to_drop));
|
||||
#else
|
||||
to_pad = _stretcher->getLatency();
|
||||
to_drop = to_pad;
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 requires padding of %2 dropping %3 (RB < v3\n", name(), to_pad, to_drop));
|
||||
#endif
|
||||
got_stretcher_padding = true;
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 requires %2 padding %3\n", name(), to_pad));
|
||||
}
|
||||
|
||||
while (to_pad > 0) {
|
||||
/* It may seem wasteful to resilence each channel
|
||||
buffer for each loop iteration here. But there's no
|
||||
inherent guarantee that passing them to the
|
||||
stretcher will leave them silent (logically, it
|
||||
must, but that's not part of the stretcher API).
|
||||
|
||||
Also, in many cases, we only actually do this once
|
||||
(depending on the ratio of the audioengine buffer
|
||||
size and the stretcher's latency).
|
||||
*/
|
||||
const samplecnt_t limit = std::min ((samplecnt_t) scratch->get_audio (0).capacity(), to_pad);
|
||||
for (uint32_t chn = 0; chn < nchans; ++chn) {
|
||||
memset (bufp[chn], 0, sizeof (Sample) * limit);
|
||||
|
|
@ -2115,10 +2145,6 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
in[chn] = data[chn % data.size ()] + read_index;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
samplecnt_t required = _stretcher->getSamplesRequired();
|
||||
samplecnt_t pre_avail = _stretcher->available ();
|
||||
#endif
|
||||
/* Note: RubberBandStretcher's process() and retrieve() API's accepts Sample**
|
||||
* as their first argument. This code may appear to only be processing the first
|
||||
* channel, but actually processes them all in one pass.
|
||||
|
|
@ -2129,17 +2155,14 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
read_index += to_stretcher;
|
||||
avail = _stretcher->available ();
|
||||
|
||||
samplecnt_t this_drop = 0;
|
||||
|
||||
if (to_drop && avail) {
|
||||
this_drop = std::min (std::min ((samplecnt_t) avail, to_drop), (samplecnt_t) scratch->get_audio (0).capacity());
|
||||
samplecnt_t this_drop = std::min (std::min ((samplecnt_t) avail, to_drop), (samplecnt_t) scratch->get_audio (0).capacity());
|
||||
_stretcher->retrieve (&bufp[0], this_drop);
|
||||
to_drop -= this_drop;
|
||||
avail = _stretcher->available ();
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 process %2 (ri now %6) at-end %3 avail %4 (was %7) of %5 (required was %8)\n",
|
||||
name(), to_stretcher, at_end, avail, nframes, read_index, pre_avail, required));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 process %2 at-end %3 avail %4 of %5\n", name(), to_stretcher, at_end, avail, nframes));
|
||||
}
|
||||
|
||||
/* we've fed the stretcher enough data to have
|
||||
|
|
@ -2157,19 +2180,18 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
}
|
||||
|
||||
/* fetch the stretch */
|
||||
samplecnt_t this_retrieve = _stretcher->retrieve (&bufp[0], from_stretcher);
|
||||
retrieved += this_retrieve;
|
||||
|
||||
retrieved += _stretcher->retrieve (&bufp[0], from_stretcher);
|
||||
|
||||
if (read_index >= last_readable_sample) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 no more data to deliver to stretcher, but with ri %8 or %9 retrieved %7(%2) to put current end at %3 vs %4 / %5 pi %6\n",
|
||||
index(), retrieved, transition_samples + retrieved, expected_end_sample, final_process_index, process_index, this_retrieve,
|
||||
read_index, last_readable_sample));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 no more data to deliver to stretcher, but retrieved %2 to put current end at %3 vs %4 / %5 pi %6\n",
|
||||
index(), retrieved, transition_samples + retrieved, expected_end_sample, final_processed_sample, process_index));
|
||||
|
||||
if (transition_samples + retrieved > expected_end_sample) {
|
||||
/* final pull from stretched data into output buffers */
|
||||
// cerr << "FS#2 from ees " << final_process_index << " - " << process_index << " & " << from_stretcher;
|
||||
from_stretcher = std::min<samplecnt_t> (from_stretcher, std::max<samplecnt_t> (0, final_process_index - process_index));
|
||||
// cerr << "FS#2 from ees " << final_processed_sample << " - " << process_index << " & " << from_stretcher;
|
||||
from_stretcher = std::min<samplecnt_t> (from_stretcher, std::max<samplecnt_t> (0, final_processed_sample - process_index));
|
||||
// cerr << " => " << from_stretcher << endl;
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 total retrieved data %2 exceeds theoretical size %3, truncate from_stretcher to %4\n",
|
||||
|
|
@ -2177,13 +2199,13 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
|
||||
if (from_stretcher == 0) {
|
||||
|
||||
if (process_index < final_process_index) {
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached (EX) end, entering playout mode to cover %2 .. %3\n", index(), process_index, final_process_index));
|
||||
if (process_index < final_processed_sample) {
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached (EX) end, entering playout mode to cover %2 .. %3\n", index(), process_index, final_processed_sample));
|
||||
_playout = true;
|
||||
} else {
|
||||
_state = Stopped;
|
||||
_loop_cnt++;
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached (EX) end, now stopped, retrieved %2, avail %3 pi %4 vs fs %5 LC now %6\n", index(), retrieved, avail, process_index, final_process_index, _loop_cnt));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached (EX) end, now stopped, retrieved %2, avail %3 pi %4 vs fs %5 LC now %6\n", index(), retrieved, avail, process_index, final_processed_sample, _loop_cnt));
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -2196,6 +2218,7 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
/* no stretch */
|
||||
assert (last_readable_sample >= read_index);
|
||||
from_stretcher = std::min<samplecnt_t> (nframes, last_readable_sample - read_index);
|
||||
// cerr << "FS#3 from lrs " << last_readable_sample << " - " << read_index << " = " << from_stretcher << endl;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -2244,8 +2267,8 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
|
||||
if (read_index >= last_readable_sample && (!do_stretch || avail <= 0)) {
|
||||
|
||||
if (process_index < final_process_index) {
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end, entering playout mode to cover %2 .. %3 avail = %4\n", index(), process_index, final_process_index, avail));
|
||||
if (process_index < final_processed_sample) {
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end, entering playout mode to cover %2 .. %3\n", index(), process_index, final_processed_sample));
|
||||
_playout = true;
|
||||
} else {
|
||||
_state = Stopped;
|
||||
|
|
@ -2253,11 +2276,6 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end, now stopped, retrieved %2, avail %3 LC now %4\n", index(), retrieved, avail, _loop_cnt));
|
||||
}
|
||||
break;
|
||||
} else if (process_index >= final_process_index) {
|
||||
_state = Stopped;
|
||||
_loop_cnt++;
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached end via pi alone, now stopped, retrieved %2, avail %3 LC now %4\n", index(), retrieved, avail, _loop_cnt));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2273,18 +2291,18 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
}
|
||||
|
||||
const pframes_t remaining_frames_for_run= orig_nframes - covered_frames;
|
||||
const pframes_t remaining_frames_till_final = final_process_index - process_index;
|
||||
const pframes_t remaining_frames_till_final = final_processed_sample - process_index;
|
||||
const pframes_t to_fill = std::min (remaining_frames_till_final, remaining_frames_for_run);
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 playout mode, remaining in run %2 till final %3 @ %5 ts %7 vs pi @ %6 to fill %4\n",
|
||||
index(), remaining_frames_for_run, remaining_frames_till_final, to_fill, final_process_index, process_index, transition_samples));
|
||||
index(), remaining_frames_for_run, remaining_frames_till_final, to_fill, final_processed_sample, process_index, transition_samples));
|
||||
|
||||
if (remaining_frames_till_final != 0) {
|
||||
|
||||
process_index += to_fill;
|
||||
covered_frames += to_fill;
|
||||
|
||||
if (process_index < final_process_index) {
|
||||
if (process_index < final_processed_sample) {
|
||||
/* more playout to be done */
|
||||
return covered_frames;
|
||||
}
|
||||
|
|
@ -2295,8 +2313,6 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 playout finished, LC now %4\n", index(), _loop_cnt));
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 run() finished, ri %2 pi %3 r/p %4\n", index(), read_index, process_index, (double) read_index / process_index));
|
||||
|
||||
if (_state == Stopped || _state == Stopping) {
|
||||
/* note: neither argument is used in the audio case */
|
||||
when_stopped_during_run (bufs, dest_offset);
|
||||
|
|
@ -2305,20 +2321,9 @@ AudioTrigger::audio_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t
|
|||
return covered_frames;
|
||||
}
|
||||
|
||||
Trigger::PendingSwap*
|
||||
AudioTrigger::pending_factory () const
|
||||
void
|
||||
AudioTrigger::reload (BufferSet&, void*)
|
||||
{
|
||||
return new AudioPendingSwap;
|
||||
}
|
||||
|
||||
int
|
||||
AudioTrigger::load_pending_data (PendingSwap& ps)
|
||||
{
|
||||
AudioPendingSwap* aps (dynamic_cast<AudioPendingSwap*> (&ps));
|
||||
assert (aps);
|
||||
std::shared_ptr<AudioRegion> ar (std::dynamic_pointer_cast<AudioRegion> (_region));
|
||||
load_data (ar, aps->audio_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2338,53 +2343,21 @@ AudioTrigger::check_edit_swap (timepos_t const & time, bool playing, BufferSet&
|
|||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 noticed pending swap @ %3\n", _box.order(), index(), pending));
|
||||
|
||||
/* Need to use the region's tempo (map) to convert between time domains here */
|
||||
|
||||
if (stretching()) {
|
||||
|
||||
Temporal::TempoMap::SharedPtr rmap (_region->tempo_map());
|
||||
assert (rmap);
|
||||
|
||||
if (pending->length.time_domain() == Temporal::BeatTime) {
|
||||
_beatcnt = Temporal::DoubleableBeats (pending->length.beats()).to_double();
|
||||
} else {
|
||||
_beatcnt = Temporal::DoubleableBeats (rmap->quarters_at_sample (pending->length.samples())).to_double();
|
||||
}
|
||||
}
|
||||
|
||||
/* Switch over data, which spans region->start() to region->end() aka
|
||||
* region->start() + region->length()
|
||||
*/
|
||||
|
||||
AudioPendingSwap* aps (dynamic_cast<AudioPendingSwap*> (pending));
|
||||
assert (aps);
|
||||
data = aps->audio_data;
|
||||
|
||||
/* pending->audio_data is now unusable */
|
||||
|
||||
if (playing) {
|
||||
|
||||
/* if the start has been moved past the current process
|
||||
* position, we need to do something drastic.
|
||||
*/
|
||||
|
||||
if (pending->play_start > process_index) {
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1/%2 new start %3 is past process index %4\n", _box.order(), index(), pending->play_start, process_index));
|
||||
jump_stop (bufs, 0);
|
||||
startup (bufs, 0, _quantization);
|
||||
return;
|
||||
}
|
||||
|
||||
Temporal::Beats elen_ignored;
|
||||
compute_end (Temporal::TempoMap::use(), _transition_bbt, transition_samples, elen_ignored);
|
||||
}
|
||||
|
||||
/* adjust read index to point to the same sample, if possible */
|
||||
|
||||
adjust_bounds (pending->play_start, pending->play_end, pending->length, true);
|
||||
old_pending_swap.store (pending);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AudioTrigger::adjust_bounds (Temporal::timepos_t const & start, Temporal::timepos_t const & end, Temporal::timecnt_t const & length, bool)
|
||||
{
|
||||
Temporal::TempoMap::SharedPtr tmap (Temporal::TempoMap::use());
|
||||
set_start (timepos_t (start.samples()));
|
||||
set_user_data_length (length.samples());
|
||||
/* XXX not 100% sure that we should set this here or not */
|
||||
_beatcnt = Temporal::DoubleableBeats (length.beats()).to_double();
|
||||
}
|
||||
|
||||
/*--------------------*/
|
||||
|
||||
MIDITrigger::MIDITrigger (uint32_t n, TriggerBox& b)
|
||||
|
|
@ -2751,7 +2724,7 @@ MIDITrigger::compute_end (Temporal::TempoMap::SharedPtr const & tmap, Temporal::
|
|||
|
||||
timepos_t e (final_beat);
|
||||
|
||||
final_process_index = e.samples() - transition_samples;
|
||||
final_processed_sample = e.samples() - transition_samples;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
|
@ -2979,6 +2952,24 @@ MIDITrigger::set_length (timecnt_t const & newlen)
|
|||
|
||||
}
|
||||
|
||||
timepos_t
|
||||
MIDITrigger::current_length() const
|
||||
{
|
||||
if (_region) {
|
||||
return timepos_t (data_length);
|
||||
}
|
||||
return timepos_t (Temporal::BeatTime);
|
||||
}
|
||||
|
||||
timepos_t
|
||||
MIDITrigger::natural_length() const
|
||||
{
|
||||
if (_region) {
|
||||
return timepos_t::from_ticks (_region->length().magnitude());
|
||||
}
|
||||
return timepos_t (Temporal::BeatTime);
|
||||
}
|
||||
|
||||
void
|
||||
MIDITrigger::estimate_midi_patches ()
|
||||
{
|
||||
|
|
@ -3125,6 +3116,11 @@ MIDITrigger::retrigger ()
|
|||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 retriggered to start, ts = %2\n", _index, transition_beats));
|
||||
}
|
||||
|
||||
void
|
||||
MIDITrigger::reload (BufferSet&, void*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MIDITrigger::tempo_map_changed ()
|
||||
{
|
||||
|
|
@ -3169,21 +3165,6 @@ MIDITrigger::tempo_map_changed ()
|
|||
map_change = true;
|
||||
}
|
||||
|
||||
Trigger::PendingSwap*
|
||||
MIDITrigger::pending_factory () const
|
||||
{
|
||||
return new MIDIPendingSwap;
|
||||
}
|
||||
|
||||
int
|
||||
MIDITrigger::load_pending_data (PendingSwap& ps)
|
||||
{
|
||||
MIDIPendingSwap* mps (dynamic_cast<MIDIPendingSwap*> (&ps));
|
||||
assert (mps);
|
||||
_model->render (_model->read_lock(), *mps->rt_midibuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MIDITrigger::model_contents_changed ()
|
||||
{
|
||||
|
|
@ -3459,28 +3440,28 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
|||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 not done with playout, all frames covered\n", index()));
|
||||
} else {
|
||||
/* finishing up playout */
|
||||
samplepos_t final_process_index = tmap->sample_at (timepos_t (final_beat));
|
||||
samplepos_t final_processed_sample = tmap->sample_at (timepos_t (final_beat));
|
||||
|
||||
if (map_change) {
|
||||
if ((start_sample > final_process_index) || (final_process_index - start_sample > orig_nframes)) {
|
||||
if ((start_sample > final_processed_sample) || (final_processed_sample - start_sample > orig_nframes)) {
|
||||
nframes = 0;
|
||||
_loop_cnt++;
|
||||
_state = Stopping;
|
||||
} else {
|
||||
nframes = orig_nframes - (final_process_index - start_sample);
|
||||
nframes = orig_nframes - (final_processed_sample - start_sample);
|
||||
}
|
||||
} else {
|
||||
nframes = orig_nframes - (final_process_index - start_sample);
|
||||
nframes = orig_nframes - (final_processed_sample - start_sample);
|
||||
_loop_cnt++;
|
||||
_state = Stopped;
|
||||
}
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 playout done, nf = %2 fb %3 fs %4 %5 LC %6\n", index(), nframes, final_beat, final_process_index, start_sample, _loop_cnt));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 playout done, nf = %2 fb %3 fs %4 %5 LC %6\n", index(), nframes, final_beat, final_processed_sample, start_sample, _loop_cnt));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const samplepos_t final_process_index = tmap->sample_at (timepos_t (final_beat));
|
||||
const samplecnt_t nproc = (final_process_index - start_sample);
|
||||
const samplepos_t final_processed_sample = tmap->sample_at (timepos_t (final_beat));
|
||||
const samplecnt_t nproc = (final_processed_sample - start_sample);
|
||||
|
||||
if (nproc > orig_nframes) {
|
||||
/* tempo map changed, probably */
|
||||
|
|
@ -3490,7 +3471,7 @@ MIDITrigger::midi_run (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
|||
}
|
||||
_loop_cnt++;
|
||||
_state = Stopped;
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached final event, now stopped, nf = %2 fb %3 fs %4 %5 LC %6\n", index(), nframes, final_beat, final_process_index, start_sample, _loop_cnt));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 reached final event, now stopped, nf = %2 fb %3 fs %4 %5 LC %6\n", index(), nframes, final_beat, final_processed_sample, start_sample, _loop_cnt));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -4938,8 +4919,6 @@ TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_samp
|
|||
|
||||
const Location* const loop_loc = _loop_location;
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("**** Triggerbox::run(%1) from %2 to %3 nf = %4\n", order(), start_sample, end_sample, nframes));
|
||||
|
||||
if (!loop_loc) {
|
||||
run_cycle (bufs, start_sample, end_sample, speed, nframes);
|
||||
} else {
|
||||
|
|
@ -4977,7 +4956,7 @@ TriggerBox::run_cycle (BufferSet& bufs, samplepos_t start_sample, samplepos_t en
|
|||
const Temporal::Beats __end_beats (timepos_t (end_sample).beats());
|
||||
const double __bpm = __tmap->quarters_per_minute_at (timepos_t (__start_beats));
|
||||
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("**** Triggerbox::run_cycle() for %6, ss %1 es %2 sb %3 eb %4 bpm %5 nf %7\n", start_sample, end_sample, __start_beats, __end_beats, __bpm, order(), nframes));
|
||||
DEBUG_TRACE (DEBUG::Triggers, string_compose ("**** Triggerbox::run() for %6, ss %1 es %2 sb %3 eb %4 bpm %5 nf %7\n", start_sample, end_sample, __start_beats, __end_beats, __bpm, order(), nframes));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -5587,6 +5566,15 @@ TriggerBox::Request::operator delete (void *ptr, size_t /*size*/)
|
|||
return pool->release (ptr);
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::request_reload (int32_t slot, void* ptr)
|
||||
{
|
||||
Request* r = new Request (Request::Reload);
|
||||
r->slot = slot;
|
||||
r->ptr = ptr;
|
||||
requests.write (&r, 1);
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::process_requests (BufferSet& bufs)
|
||||
{
|
||||
|
|
@ -5603,11 +5591,23 @@ TriggerBox::process_request (BufferSet& bufs, Request* req)
|
|||
switch (req->type) {
|
||||
case Request::Use:
|
||||
break;
|
||||
case Request::Reload:
|
||||
reload (bufs, req->slot, req->ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
delete req; /* back to the pool, RT-safe */
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::reload (BufferSet& bufs, int32_t slot, void* ptr)
|
||||
{
|
||||
if (slot >= (int32_t) all_triggers.size()) {
|
||||
return;
|
||||
}
|
||||
all_triggers[slot]->reload (bufs, ptr);
|
||||
}
|
||||
|
||||
double
|
||||
TriggerBox::position_as_fraction () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
ardour { ["type"] = "EditorAction", name = "Brutalize MIDI",
|
||||
license = "MIT",
|
||||
author = "Ardour Team",
|
||||
description = [[Randomize MIDI Note position (de-quantize) of selected MIDI regions.]]
|
||||
description = [[Randomize MIDI Note position (de-quantize).]]
|
||||
}
|
||||
|
||||
function factory () return function ()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue