#include <glibmm/threads.h>
#include "pbd/xml++.h"
+#include "pbd/types_convert.h"
#include "ardour/debug.h"
#include "ardour/filter.h"
#include "ardour/source.h"
#include "ardour/tempo.h"
#include "ardour/transient_detector.h"
+#include "ardour/types_convert.h"
#include "pbd/i18n.h"
PBD::PropertyDescriptor<bool> hidden;
PBD::PropertyDescriptor<bool> position_locked;
PBD::PropertyDescriptor<bool> valid_transients;
- PBD::PropertyDescriptor<framepos_t> start;
- PBD::PropertyDescriptor<framecnt_t> length;
- PBD::PropertyDescriptor<framepos_t> position;
+ PBD::PropertyDescriptor<samplepos_t> start;
+ PBD::PropertyDescriptor<samplecnt_t> length;
+ PBD::PropertyDescriptor<samplepos_t> position;
PBD::PropertyDescriptor<double> beat;
- PBD::PropertyDescriptor<framecnt_t> sync_position;
+ PBD::PropertyDescriptor<samplecnt_t> sync_position;
PBD::PropertyDescriptor<layer_t> layer;
- PBD::PropertyDescriptor<framepos_t> ancestral_start;
- PBD::PropertyDescriptor<framecnt_t> ancestral_length;
+ PBD::PropertyDescriptor<samplepos_t> ancestral_start;
+ PBD::PropertyDescriptor<samplecnt_t> ancestral_length;
PBD::PropertyDescriptor<float> stretch;
PBD::PropertyDescriptor<float> shift;
PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
, _position (Properties::position, 0) \
, _beat (Properties::beat, 0.0) \
, _sync_position (Properties::sync_position, (s)) \
- , _pulse (0.0) \
+ , _quarter_note (0.0) \
, _transient_user_start (0) \
, _transient_analysis_start (0) \
, _transient_analysis_end (0) \
, _position(Properties::position, other->_position) \
, _beat (Properties::beat, other->_beat) \
, _sync_position(Properties::sync_position, other->_sync_position) \
- , _pulse (other->_pulse) \
+ , _quarter_note (other->_quarter_note) \
, _user_transients (other->_user_transients) \
, _transient_user_start (other->_transient_user_start) \
, _transients (other->_transients) \
, _transient_analysis_start (other->_transient_analysis_start) \
, _transient_analysis_end (other->_transient_analysis_end) \
+ , _soloSelected (false) \
, _muted (Properties::muted, other->_muted) \
, _opaque (Properties::opaque, other->_opaque) \
, _locked (Properties::locked, other->_locked) \
, _layering_index (Properties::layering_index, other->_layering_index)
/* derived-from-derived constructor (no sources in constructor) */
-Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
+Region::Region (Session& s, samplepos_t start, samplecnt_t length, const string& name, DataType type)
: SessionObject(s, name)
, _type(type)
, REGION_DEFAULT_STATE(start,length)
_start = other->_start;
_beat = other->_beat;
- _pulse = other->_pulse;
+ _quarter_note = other->_quarter_note;
/* sync pos is relative to start of file. our start-in-file is now zero,
so set our sync position to whatever the the difference between
the start within \a other is given by \a offset
(i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
*/
-Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset, const int32_t sub_num)
+Region::Region (boost::shared_ptr<const Region> other, MusicSample offset)
: SessionObject(other->session(), other->name())
, _type (other->data_type())
, REGION_COPY_STATE (other)
/* override state that may have been incorrectly inherited from the other region
*/
- _position = other->_position + offset;
_locked = false;
_whole_file = false;
_hidden = false;
use_sources (other->_sources);
set_master_sources (other->_master_sources);
- _start = other->_start + offset;
- _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
- _pulse = _session.tempo_map().exact_qn_at_frame (_position, sub_num) / 4.0;
+ _position = other->_position + offset.sample;
+ _start = other->_start + offset.sample;
+
+ /* prevent offset of 0 from altering musical position */
+ if (offset.sample != 0) {
+ const double offset_qn = _session.tempo_map().exact_qn_at_sample (other->_position + offset.sample, offset.division)
+ - other->_quarter_note;
+
+ _quarter_note = other->_quarter_note + offset_qn;
+ _beat = _session.tempo_map().beat_at_quarter_note (_quarter_note);
+ } else {
+ _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
+ }
/* if the other region had a distinct sync point
set, then continue to use it as best we can.
}
void
-Region::set_length (framecnt_t len, const int32_t sub_num)
+Region::set_selected_for_solo(bool yn)
+{
+ if ( _soloSelected != yn) {
+
+ boost::shared_ptr<Playlist> pl (playlist());
+ if (pl){
+ if (yn) {
+ pl->AddToSoloSelectedList(this);
+ } else {
+ pl->RemoveFromSoloSelectedList(this);
+ }
+ }
+
+ _soloSelected = yn;
+ }
+
+}
+
+void
+Region::set_length (samplecnt_t len, const int32_t sub_num)
{
//cerr << "Region::set_length() len = " << len << endl;
if (locked()) {
length impossible.
*/
- if (max_framepos - len < _position) {
+ if (max_samplepos - len < _position) {
return;
}
}
void
-Region::set_length_internal (framecnt_t len, const int32_t sub_num)
+Region::set_length_internal (samplecnt_t len, const int32_t sub_num)
{
_last_length = _length;
_length = len;
}
void
-Region::special_set_position (framepos_t pos)
+Region::special_set_position (samplepos_t pos)
{
/* this is used when creating a whole file region as
a way to store its "natural" or "captured" position.
_position_lock_style = ps;
- if (_position_lock_style == MusicTime) {
- _beat = _session.tempo_map().beat_at_frame (_position);
- _pulse = _session.tempo_map().pulse_at_frame (_position);
- }
-
send_change (Properties::position_lock_style);
}
}
return;
}
- const framepos_t pos = _session.tempo_map().frame_at_beat (_beat);
- /* we have _beat. update frame position non-musically */
+ /* prevent movement before 0 */
+ const samplepos_t pos = max ((samplepos_t) 0, _session.tempo_map().sample_at_beat (_beat));
+ /* we have _beat. update sample position non-musically */
set_position_internal (pos, false, 0);
/* do this even if the position is the same. this helps out
}
void
-Region::set_position (framepos_t pos, int32_t sub_num)
+Region::set_position (samplepos_t pos, int32_t sub_num)
{
if (!can_move()) {
return;
}
- if (sub_num == 0) {
- set_position_internal (pos, true, 0);
- } else {
- double beat = _session.tempo_map().exact_beat_at_frame (pos, sub_num);
- _beat = beat;
- _pulse = _session.tempo_map().exact_qn_at_frame (pos, sub_num) / 4.0;
- set_position_internal (pos, false, sub_num);
- }
-
/* do this even if the position is the same. this helps out
a GUI that has moved its representation already.
*/
PropertyChange p_and_l;
p_and_l.add (Properties::position);
- /* Currently length change due to position change is only implemented
- for MidiRegion (Region has no length in beats).
- Notify a length change regardless (its more efficient for MidiRegions),
- and when Region has a _length_beats we will need it here anyway).
- */
- p_and_l.add (Properties::length);
+
+ if (position_lock_style() == AudioTime) {
+ set_position_internal (pos, true, sub_num);
+ } else {
+ if (!_session.loading()) {
+ _beat = _session.tempo_map().exact_beat_at_sample (pos, sub_num);
+ _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
+ }
+
+ set_position_internal (pos, false, sub_num);
+ }
+
+ if (position_lock_style() == MusicTime) {
+ p_and_l.add (Properties::length);
+ }
send_change (p_and_l);
}
-/** A gui may need to create a region, then place it in an initial
- * position determined by the user.
- * When this takes place within one gui operation, we have to reset
- * _last_position to prevent an implied move.
- */
void
-Region::set_initial_position (framepos_t pos)
+Region::set_position_internal (samplepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
{
- if (!can_move()) {
- return;
- }
+ /* We emit a change of Properties::position even if the position hasn't changed
+ (see Region::set_position), so we must always set this up so that
+ e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
+ */
+ _last_position = _position;
if (_position != pos) {
_position = pos;
+ if (allow_bbt_recompute) {
+ recompute_position_from_lock_style (sub_num);
+ } else {
+ /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
+ _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
+ }
+
/* check that the new _position wouldn't make the current
length impossible - if so, change the length.
XXX is this the right thing to do?
*/
-
- if (max_framepos - _length < _position) {
+ if (max_samplepos - _length < _position) {
_last_length = _length;
- _length = max_framepos - _position;
+ _length = max_samplepos - _position;
}
-
- recompute_position_from_lock_style (0);
- /* ensure that this move doesn't cause a range move */
- _last_position = _position;
}
+}
+void
+Region::set_position_music (double qn)
+{
+ if (!can_move()) {
+ return;
+ }
/* do this even if the position is the same. this helps out
a GUI that has moved its representation already.
*/
- send_change (Properties::position);
+ PropertyChange p_and_l;
+
+ p_and_l.add (Properties::position);
+
+ if (!_session.loading()) {
+ _beat = _session.tempo_map().beat_at_quarter_note (qn);
+ }
+
+ /* will set sample accordingly */
+ set_position_music_internal (qn);
+
+ if (position_lock_style() == MusicTime) {
+ p_and_l.add (Properties::length);
+ }
+
+ send_change (p_and_l);
}
void
-Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute, const int32_t sub_num)
+Region::set_position_music_internal (double qn)
{
/* We emit a change of Properties::position even if the position hasn't changed
(see Region::set_position), so we must always set this up so that
*/
_last_position = _position;
- if (_position != pos) {
- _position = pos;
+ if (_quarter_note != qn) {
+ _position = _session.tempo_map().sample_at_quarter_note (qn);
+ _quarter_note = qn;
- if (allow_bbt_recompute) {
- recompute_position_from_lock_style (sub_num);
- } else {
- /* MusicTime dictates that we glue to ardour beats. the pulse may have changed.*/
- _pulse = _session.tempo_map().pulse_at_beat (_beat);
+ /* check that the new _position wouldn't make the current
+ length impossible - if so, change the length.
+
+ XXX is this the right thing to do?
+ */
+ if (max_samplepos - _length < _position) {
+ _last_length = _length;
+ _length = max_samplepos - _position;
}
+ }
+}
+
+/** A gui may need to create a region, then place it in an initial
+ * position determined by the user.
+ * When this takes place within one gui operation, we have to reset
+ * _last_position to prevent an implied move.
+ */
+void
+Region::set_initial_position (samplepos_t pos)
+{
+ if (!can_move()) {
+ return;
+ }
+
+ if (_position != pos) {
+ _position = pos;
/* check that the new _position wouldn't make the current
length impossible - if so, change the length.
XXX is this the right thing to do?
*/
- if (max_framepos - _length < _position) {
+
+ if (max_samplepos - _length < _position) {
_last_length = _length;
- _length = max_framepos - _position;
+ _length = max_samplepos - _position;
}
+
+ recompute_position_from_lock_style (0);
+ /* ensure that this move doesn't cause a range move */
+ _last_position = _position;
}
+
+
+ /* do this even if the position is the same. this helps out
+ a GUI that has moved its representation already.
+ */
+ send_change (Properties::position);
}
void
Region::recompute_position_from_lock_style (const int32_t sub_num)
{
- _beat = _session.tempo_map().exact_beat_at_frame (_position, sub_num);
- _pulse = _session.tempo_map().exact_qn_at_frame (_position, sub_num) / 4.0;
+ _beat = _session.tempo_map().exact_beat_at_sample (_position, sub_num);
+ _quarter_note = _session.tempo_map().exact_qn_at_sample (_position, sub_num);
}
void
-Region::nudge_position (frameoffset_t n)
+Region::nudge_position (sampleoffset_t n)
{
if (locked() || video_locked()) {
return;
return;
}
- framepos_t new_position = _position;
+ samplepos_t new_position = _position;
if (n > 0) {
- if (_position > max_framepos - n) {
- new_position = max_framepos;
+ if (_position > max_samplepos - n) {
+ new_position = max_samplepos;
} else {
new_position += n;
}
}
void
-Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
+Region::set_ancestral_data (samplepos_t s, samplecnt_t l, float st, float sh)
{
_ancestral_length = l;
_ancestral_start = s;
}
void
-Region::set_start (framepos_t pos)
+Region::set_start (samplepos_t pos)
{
if (locked() || position_locked() || video_locked()) {
return;
}
void
-Region::move_start (frameoffset_t distance, const int32_t sub_num)
+Region::move_start (sampleoffset_t distance, const int32_t sub_num)
{
if (locked() || position_locked() || video_locked()) {
return;
}
- framepos_t new_start;
+ samplepos_t new_start;
if (distance > 0) {
- if (_start > max_framepos - distance) {
- new_start = max_framepos; // makes no sense
+ if (_start > max_samplepos - distance) {
+ new_start = max_samplepos; // makes no sense
} else {
new_start = _start + distance;
}
}
void
-Region::trim_front (framepos_t new_position, const int32_t sub_num)
+Region::trim_front (samplepos_t new_position, const int32_t sub_num)
{
modify_front (new_position, false, sub_num);
}
void
-Region::cut_front (framepos_t new_position, const int32_t sub_num)
+Region::cut_front (samplepos_t new_position, const int32_t sub_num)
{
modify_front (new_position, true, sub_num);
}
void
-Region::cut_end (framepos_t new_endpoint, const int32_t sub_num)
+Region::cut_end (samplepos_t new_endpoint, const int32_t sub_num)
{
modify_end (new_endpoint, true, sub_num);
}
void
-Region::modify_front (framepos_t new_position, bool reset_fade, const int32_t sub_num)
+Region::modify_front (samplepos_t new_position, bool reset_fade, const int32_t sub_num)
{
if (locked()) {
return;
}
- framepos_t end = last_frame();
- framepos_t source_zero;
+ samplepos_t end = last_sample();
+ samplepos_t source_zero;
if (_position > _start) {
source_zero = _position - _start;
if (new_position < end) { /* can't trim it zero or negative length */
- framecnt_t newlen = 0;
+ samplecnt_t newlen = 0;
if (!can_trim_start_before_source_start ()) {
/* can't trim it back past where source position zero is located */
}
void
-Region::modify_end (framepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
+Region::modify_end (samplepos_t new_endpoint, bool reset_fade, const int32_t sub_num)
{
if (locked()) {
return;
*/
void
-Region::trim_end (framepos_t new_endpoint, const int32_t sub_num)
+Region::trim_end (samplepos_t new_endpoint, const int32_t sub_num)
{
modify_end (new_endpoint, false, sub_num);
}
void
-Region::trim_to (framepos_t position, framecnt_t length, const int32_t sub_num)
+Region::trim_to (samplepos_t position, samplecnt_t length, const int32_t sub_num)
{
if (locked()) {
return;
}
void
-Region::trim_to_internal (framepos_t position, framecnt_t length, const int32_t sub_num)
+Region::trim_to_internal (samplepos_t position, samplecnt_t length, const int32_t sub_num)
{
- framepos_t new_start;
+ samplepos_t new_start;
if (locked()) {
return;
}
- frameoffset_t const start_shift = position - _position;
+ sampleoffset_t const start_shift = position - _position;
if (start_shift > 0) {
- if (_start > max_framepos - start_shift) {
- new_start = max_framepos;
+ if (_start > max_samplepos - start_shift) {
+ new_start = max_samplepos;
} else {
new_start = _start + start_shift;
}
/* Set position before length, otherwise for MIDI regions this bad thing happens:
* 1. we call set_length_internal; length in beats is computed using the region's current
* (soon-to-be old) position
- * 2. we call set_position_internal; position is set and length in frames re-computed using
+ * 2. we call set_position_internal; position is set and length in samples re-computed using
* length in beats from (1) but at the new position, which is wrong if the region
* straddles a tempo/meter change.
*/
* @param absolute_pos Session time.
*/
void
-Region::set_sync_position (framepos_t absolute_pos)
+Region::set_sync_position (samplepos_t absolute_pos)
{
/* position within our file */
- framepos_t const file_pos = _start + (absolute_pos - _position);
+ samplepos_t const file_pos = _start + (absolute_pos - _position);
if (file_pos != _sync_position) {
_sync_marked = true;
}
}
-/* @return the sync point relative the first frame of the region */
-frameoffset_t
+/* @return the sync point relative the first sample of the region */
+sampleoffset_t
Region::sync_offset (int& dir) const
{
if (sync_marked()) {
}
}
-framepos_t
-Region::adjust_to_sync (framepos_t pos) const
+samplepos_t
+Region::adjust_to_sync (samplepos_t pos) const
{
int sync_dir;
- frameoffset_t offset = sync_offset (sync_dir);
+ sampleoffset_t offset = sync_offset (sync_dir);
// cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
pos = 0;
}
} else {
- if (max_framepos - pos > offset) {
+ if (max_samplepos - pos > offset) {
pos += offset;
}
}
}
/** @return Sync position in session time */
-framepos_t
+samplepos_t
Region::sync_position() const
{
if (sync_marked()) {
Region::state ()
{
XMLNode *node = new XMLNode ("Region");
- char buf[64];
char buf2[64];
- LocaleGuard lg;
- const char* fe = NULL;
/* custom version of 'add_properties (*node);'
* skip values that have have dedicated save functions
i->second->get_value (*node);
}
- id().print (buf, sizeof (buf));
- node->add_property ("id", buf);
- node->add_property ("type", _type.to_string());
+ node->set_property ("id", id ());
+ node->set_property ("type", _type);
+
+ std::string fe;
switch (_first_edit) {
case EditChangesNothing:
break;
}
- node->add_property ("first-edit", fe);
+ node->set_property ("first-edit", fe);
/* note: flags are stored by derived classes */
for (uint32_t n=0; n < _sources.size(); ++n) {
snprintf (buf2, sizeof(buf2), "source-%d", n);
- _sources[n]->id().print (buf, sizeof(buf));
- node->add_property (buf2, buf);
+ node->set_property (buf2, _sources[n]->id());
}
for (uint32_t n=0; n < _master_sources.size(); ++n) {
snprintf (buf2, sizeof(buf2), "master-source-%d", n);
- _master_sources[n]->id().print (buf, sizeof (buf));
- node->add_property (buf2, buf);
+ node->set_property (buf2, _master_sources[n]->id ());
}
/* Only store nested sources for the whole-file region that acts
int
Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
{
- XMLProperty const * prop;
Timecode::BBT_Time bbt_time;
Stateful::save_extra_xml (node);
set_id (node);
if (_position_lock_style == MusicTime) {
- if ((prop = node.property ("bbt-position")) != 0) {
- if (sscanf (prop->value().c_str(), "%d|%d|%d",
+ std::string bbt_str;
+ if (node.get_property ("bbt-position", bbt_str)) {
+ if (sscanf (bbt_str.c_str(), "%d|%d|%d",
&bbt_time.bars,
&bbt_time.beats,
&bbt_time.ticks) != 3) {
_position_lock_style = AudioTime;
- _beat = _session.tempo_map().beat_at_frame (_position);
+ _beat = _session.tempo_map().beat_at_sample (_position);
} else {
_beat = _session.tempo_map().beat_at_bbt (bbt_time);
}
+ /* no position property change for legacy Property, so we do this here */
+ _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
}
}
- _pulse = _session.tempo_map().pulse_at_beat (_beat);
-
/* fix problems with old sessions corrupted by impossible
values for _stretch or _shift
*/
}
/* Quick fix for 2.x sessions when region is muted */
- if ((prop = node.property (X_("flags")))) {
- if (string::npos != prop->value().find("Muted")){
+ std::string flags;
+ if (node.get_property (X_("flags"), flags)) {
+ if (string::npos != flags.find("Muted")){
set_muted (true);
}
}
bool
Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
{
- return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
+ return coverage (other->first_sample(), other->last_sample()) != Evoral::OverlapNone;
}
bool
}
-framecnt_t
+samplecnt_t
Region::source_length(uint32_t n) const
{
assert (n < _sources.size());
}
bool
-Region::verify_length (framecnt_t& len)
+Region::verify_length (samplecnt_t& len)
{
if (source() && (source()->destructive() || source()->length_mutable())) {
return true;
}
- framecnt_t maxlen = 0;
+ samplecnt_t maxlen = 0;
for (uint32_t n = 0; n < _sources.size(); ++n) {
maxlen = max (maxlen, source_length(n) - _start);
}
bool
-Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
+Region::verify_start_and_length (samplepos_t new_start, samplecnt_t& new_length)
{
if (source() && (source()->destructive() || source()->length_mutable())) {
return true;
}
- framecnt_t maxlen = 0;
+ samplecnt_t maxlen = 0;
for (uint32_t n = 0; n < _sources.size(); ++n) {
maxlen = max (maxlen, source_length(n) - new_start);
}
bool
-Region::verify_start (framepos_t pos)
+Region::verify_start (samplepos_t pos)
{
if (source() && (source()->destructive() || source()->length_mutable())) {
return true;
}
bool
-Region::verify_start_mutable (framepos_t& new_start)
+Region::verify_start_mutable (samplepos_t& new_start)
{
if (source() && (source()->destructive() || source()->length_mutable())) {
return true;
if (cnt > 1 ) {
afl.sort ();
// remove exact duplicates
- TransientDetector::cleanup_transients (afl, _session.frame_rate(), 0);
+ TransientDetector::cleanup_transients (afl, _session.sample_rate(), 0);
}
}
}
void
-Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const frameoffset_t off) const
+Region::merge_features (AnalysisFeatureList& result, const AnalysisFeatureList& src, const sampleoffset_t off) const
{
for (AnalysisFeatureList::const_iterator x = src.begin(); x != src.end(); ++x) {
- const frameoffset_t p = (*x) + off;
- if (p < first_frame() || p > last_frame()) {
+ const sampleoffset_t p = (*x) + off;
+ if (p < first_sample() || p > last_sample()) {
continue;
}
result.push_back (p);
void
Region::post_set (const PropertyChange& pc)
{
- if (pc.contains (Properties::position)) {
- recompute_position_from_lock_style (0);
- }
+ _quarter_note = _session.tempo_map().quarter_note_at_beat (_beat);
}
void
-Region::set_start_internal (framecnt_t s, const int32_t sub_num)
+Region::set_start_internal (samplecnt_t s, const int32_t sub_num)
{
_start = s;
}
-framepos_t
+samplepos_t
Region::earliest_possible_position () const
{
if (_start > _position) {
}
}
-framecnt_t
-Region::latest_possible_frame () const
+samplecnt_t
+Region::latest_possible_sample () const
{
- framecnt_t minlen = max_framecnt;
+ samplecnt_t minlen = max_samplecnt;
for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
/* non-audio regions have a length that may vary based on their
minlen = min (minlen, (*i)->length (_position));
}
- /* the latest possible last frame is determined by the current
+ /* the latest possible last sample is determined by the current
* position, plus the shortest source extent past _start.
*/