summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2010-05-19 03:03:28 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2010-05-19 03:03:28 +0000
commite258b2622a4386b405c2216d79b34887c3ed55bf (patch)
treec2abdacc5a31e9d572257050256c704b41fb46f5 /libs
parentc25c7598c134af88bb85b5690aabc35472c77adf (diff)
MIDI region forking, plus Playlist::regions_to_read() fix forward ported from 2.X. region forking requires a few cleanups
git-svn-id: svn://localhost/ardour2/branches/3.0@7118 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/audioregion.h1
-rw-r--r--libs/ardour/ardour/automatable.h1
-rw-r--r--libs/ardour/ardour/automatable_sequence.h9
-rw-r--r--libs/ardour/ardour/debug.h1
-rw-r--r--libs/ardour/ardour/midi_model.h5
-rw-r--r--libs/ardour/ardour/midi_region.h2
-rw-r--r--libs/ardour/ardour/midi_source.h3
-rw-r--r--libs/ardour/ardour/midi_state_tracker.h6
-rw-r--r--libs/ardour/ardour/utils.h2
-rw-r--r--libs/ardour/audio_playlist.cc5
-rw-r--r--libs/ardour/audioregion.cc6
-rw-r--r--libs/ardour/automatable.cc12
-rw-r--r--libs/ardour/debug.cc1
-rw-r--r--libs/ardour/midi_model.cc64
-rw-r--r--libs/ardour/midi_region.cc23
-rw-r--r--libs/ardour/midi_source.cc80
-rw-r--r--libs/ardour/midi_state_tracker.cc30
-rw-r--r--libs/ardour/playlist.cc38
-rw-r--r--libs/ardour/route.cc2
-rw-r--r--libs/ardour/session_state.cc3
-rw-r--r--libs/ardour/utils.cc19
-rw-r--r--libs/evoral/evoral/ControlSet.hpp1
-rw-r--r--libs/evoral/evoral/MIDIEvent.hpp1
-rw-r--r--libs/evoral/evoral/Sequence.hpp1
-rw-r--r--libs/evoral/evoral/types.hpp3
-rw-r--r--libs/evoral/src/ControlSet.cpp6
-rw-r--r--libs/evoral/src/Sequence.cpp26
27 files changed, 288 insertions, 63 deletions
diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h
index f9c13b2bbe..0a3e53039e 100644
--- a/libs/ardour/ardour/audioregion.h
+++ b/libs/ardour/ardour/audioregion.h
@@ -107,7 +107,6 @@ class AudioRegion : public Region
};
virtual framecnt_t read (Sample*, framepos_t pos, framecnt_t cnt, int channel) const;
- virtual framecnt_t read_with_ops (Sample*, framepos_t pos, framecnt_t cnt, int channel, ReadOps rops) const;
virtual framecnt_t readable_length() const { return length(); }
virtual framecnt_t read_at (Sample *buf, Sample *mixdown_buf, float *gain_buf,
diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h
index 3236d816c3..9b83705b0a 100644
--- a/libs/ardour/ardour/automatable.h
+++ b/libs/ardour/ardour/automatable.h
@@ -42,6 +42,7 @@ class Automatable : virtual public Evoral::ControlSet
{
public:
Automatable(Session&);
+ Automatable (const Automatable& other);
Automatable();
virtual ~Automatable() {}
diff --git a/libs/ardour/ardour/automatable_sequence.h b/libs/ardour/ardour/automatable_sequence.h
index 88e1733c1b..730ea33a7c 100644
--- a/libs/ardour/ardour/automatable_sequence.h
+++ b/libs/ardour/ardour/automatable_sequence.h
@@ -29,11 +29,18 @@ namespace ARDOUR {
template<typename T>
class AutomatableSequence : public Automatable, public Evoral::Sequence<T> {
public:
- AutomatableSequence(Session& s, size_t /*size*/)
+ AutomatableSequence(Session& s)
: Evoral::ControlSet()
, Automatable(s)
, Evoral::Sequence<T>(EventTypeMap::instance())
{}
+
+ AutomatableSequence(const AutomatableSequence<T>& other)
+ : Evoral::ControlSet(other)
+ , Automatable(other._a_session)
+ , Evoral::Sequence<T>(other)
+ {}
+
};
} // namespace ARDOUR
diff --git a/libs/ardour/ardour/debug.h b/libs/ardour/ardour/debug.h
index 57b66a8793..1cd8854fea 100644
--- a/libs/ardour/ardour/debug.h
+++ b/libs/ardour/ardour/debug.h
@@ -46,6 +46,7 @@ namespace PBD {
extern uint64_t MidiClock;
extern uint64_t Monitor;
extern uint64_t Solo;
+ extern uint64_t AudioPlayback;
}
}
diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h
index fcd285f98c..8d949cadbb 100644
--- a/libs/ardour/ardour/midi_model.h
+++ b/libs/ardour/ardour/midi_model.h
@@ -51,7 +51,7 @@ class MidiModel : public AutomatableSequence<Evoral::MusicalTime> {
public:
typedef double TimeType;
- MidiModel(MidiSource* s, size_t size=0);
+ MidiModel(MidiSource* s);
NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); }
void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); };
@@ -151,7 +151,8 @@ public:
void apply_command(Session& session, Command* cmd);
void apply_command_as_subcommand(Session& session, Command* cmd);
- bool write_to(boost::shared_ptr<MidiSource> source);
+ bool write_to(boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin = Evoral::MinMusicalTime,
+ Evoral::MusicalTime end = Evoral::MaxMusicalTime);
// MidiModel doesn't use the normal AutomationList serialisation code
// since controller data is stored in the .mid
diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h
index 671e4fca43..568638ed21 100644
--- a/libs/ardour/ardour/midi_region.h
+++ b/libs/ardour/ardour/midi_region.h
@@ -50,6 +50,8 @@ class MidiRegion : public Region
public:
~MidiRegion();
+ boost::shared_ptr<MidiRegion> clone ();
+
boost::shared_ptr<MidiSource> midi_source (uint32_t n=0) const;
/* Stub Readable interface */
diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h
index 7b867c70fd..2484d3575a 100644
--- a/libs/ardour/ardour/midi_source.h
+++ b/libs/ardour/ardour/midi_source.h
@@ -47,6 +47,9 @@ class MidiSource : virtual public Source
MidiSource (Session& session, const XMLNode&);
virtual ~MidiSource ();
+ boost::shared_ptr<MidiSource> clone (Evoral::MusicalTime begin = Evoral::MinMusicalTime,
+ Evoral::MusicalTime end = Evoral::MaxMusicalTime);
+
/** Read the data in a given time range from the MIDI source.
* All time stamps in parameters are in audio frames (even if the source has tempo time).
* \param dst Ring buffer where read events are written
diff --git a/libs/ardour/ardour/midi_state_tracker.h b/libs/ardour/ardour/midi_state_tracker.h
index 76a669838a..5c65c1f018 100644
--- a/libs/ardour/ardour/midi_state_tracker.h
+++ b/libs/ardour/ardour/midi_state_tracker.h
@@ -28,7 +28,7 @@ template <typename T> class EventSink;
}
namespace ARDOUR {
-
+class MidiSource;
/** Tracks played notes, so they can be resolved in potential stuck note
* situations (e.g. looping, transport stop, etc).
@@ -43,10 +43,14 @@ public:
void remove (uint8_t note, uint8_t chn);
void resolve_notes (MidiBuffer& buffer, nframes64_t time);
void resolve_notes (Evoral::EventSink<nframes_t>& buffer, nframes64_t time);
+ void resolve_notes (MidiSource& src, Evoral::MusicalTime time);
void dump (std::ostream&);
void reset ();
bool empty() const { return _on == 0; }
uint16_t on() const { return _on; }
+ bool active (uint8_t note, uint8_t channel) {
+ return _active_notes[(channel*128)+note] > 0;
+ }
private:
void track_note_onoffs(const Evoral::MIDIEvent<MidiBuffer::TimeType>& event);
diff --git a/libs/ardour/ardour/utils.h b/libs/ardour/ardour/utils.h
index e68f7a01df..e7e8f2b820 100644
--- a/libs/ardour/ardour/utils.h
+++ b/libs/ardour/ardour/utils.h
@@ -50,7 +50,7 @@ static inline float f_max(float x, float a) {
return (x);
}
-std::string bump_name_once(std::string s);
+std::string bump_name_once(const std::string& s, char delimiter);
int cmp_nocase (const std::string& s, const std::string& s2);
diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc
index 65b05b7c92..bd09af4e1f 100644
--- a/libs/ardour/audio_playlist.cc
+++ b/libs/ardour/audio_playlist.cc
@@ -23,6 +23,7 @@
#include "ardour/types.h"
+#include "ardour/debug.h"
#include "ardour/configuration.h"
#include "ardour/audioplaylist.h"
#include "ardour/audioregion.h"
@@ -163,7 +164,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
if ((*i)->coverage (start, end) != OverlapNone) {
relevant_regions[(*i)->layer()].push_back (*i);
relevant_layers.push_back ((*i)->layer());
- }
+ }
}
for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
@@ -188,8 +189,10 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
+
for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
assert(ar);
ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
_read_data_count += ar->read_data_count();
diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc
index c8e742555c..0ef79b7018 100644
--- a/libs/ardour/audioregion.cc
+++ b/libs/ardour/audioregion.cc
@@ -318,12 +318,6 @@ AudioRegion::read (Sample* buf, framepos_t timeline_position, framecnt_t cnt, in
}
framecnt_t
-AudioRegion::read_with_ops (Sample* buf, framepos_t file_position, framecnt_t cnt, int channel, ReadOps rops) const
-{
- return _read_at (_sources, _length, buf, 0, 0, file_position, cnt, channel, 0, 0, rops);
-}
-
-framecnt_t
AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
framepos_t file_position, framecnt_t cnt, uint32_t chan_n,
framecnt_t read_frames, framecnt_t skip_frames) const
diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc
index 1ef39a61f0..3d705b3ea7 100644
--- a/libs/ardour/automatable.cc
+++ b/libs/ardour/automatable.cc
@@ -49,6 +49,18 @@ Automatable::Automatable(Session& session)
{
}
+Automatable::Automatable (const Automatable& other)
+ : ControlSet (other)
+ , _a_session (other._a_session)
+ , _last_automation_snapshot (0)
+{
+ Glib::Mutex::Lock lm (other._control_lock);
+
+ for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
+ boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
+ _controls[ac->parameter()] = ac;
+ }
+}
int
Automatable::old_set_automation_state (const XMLNode& node)
{
diff --git a/libs/ardour/debug.cc b/libs/ardour/debug.cc
index 268ec5d033..419579b977 100644
--- a/libs/ardour/debug.cc
+++ b/libs/ardour/debug.cc
@@ -43,4 +43,5 @@ uint64_t PBD::DEBUG::MackieControl = PBD::new_debug_bit ("mackiecontrol");
uint64_t PBD::DEBUG::MidiClock = PBD::new_debug_bit ("midiclock");
uint64_t PBD::DEBUG::Monitor = PBD::new_debug_bit ("monitor");
uint64_t PBD::DEBUG::Solo = PBD::new_debug_bit ("solo");
+uint64_t PBD::DEBUG::AudioPlayback = PBD::new_debug_bit ("audioplayback");
diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc
index ac3360ea2b..88ef60e8e5 100644
--- a/libs/ardour/midi_model.cc
+++ b/libs/ardour/midi_model.cc
@@ -30,6 +30,7 @@
#include "ardour/midi_model.h"
#include "ardour/midi_source.h"
+#include "ardour/midi_state_tracker.h"
#include "ardour/smf_source.h"
#include "ardour/types.h"
#include "ardour/session.h"
@@ -38,8 +39,8 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
-MidiModel::MidiModel(MidiSource* s, size_t size)
- : AutomatableSequence<TimeType>(s->session(), size)
+MidiModel::MidiModel(MidiSource* s)
+ : AutomatableSequence<TimeType>(s->session())
, _midi_source(s)
{
}
@@ -675,7 +676,7 @@ MidiModel::DiffCommand::get_state ()
return *diff_command;
}
-/** Write the model to a MidiSource (i.e. save the model).
+/** Write part or all of the model to a MidiSource (i.e. save the model).
* This is different from manually using read to write to a source in that
* note off events are written regardless of the track mode. This is so the
* user can switch a recorded track (with note durations from some instrument)
@@ -683,9 +684,11 @@ MidiModel::DiffCommand::get_state ()
* destroying the original note durations.
*/
bool
-MidiModel::write_to(boost::shared_ptr<MidiSource> source)
+MidiModel::write_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
{
ReadLock lock(read_lock());
+ MidiStateTracker mst;
+ Evoral::MusicalTime extra_note_on_time = end_time;
const bool old_percussive = percussive();
set_percussive(false);
@@ -694,8 +697,57 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
- source->append_event_unlocked_beats(*i);
- }
+ const Evoral::Event<Evoral::MusicalTime>& ev (*i);
+
+ if (ev.time() >= begin_time && ev.time() < end_time) {
+
+ const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
+ static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
+
+ if (!mev) {
+ continue;
+ }
+
+
+ if (mev->is_note_off()) {
+
+ if (!mst.active (mev->note(), mev->channel())) {
+
+ /* add a note-on at the start of the range we're writing
+ to the file. velocity is just an arbitary reasonable value.
+ */
+
+ Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
+ on.set_type (mev->type());
+ on.set_note (mev->note());
+ on.set_channel (mev->channel());
+ on.set_velocity (mev->velocity());
+
+ cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
+ source->append_event_unlocked_beats (on);
+ mst.add (on.note(), on.channel());
+ mst.dump (cerr);
+ extra_note_on_time += 1.0/128.0;
+ }
+
+ cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
+ source->append_event_unlocked_beats (*i);
+ mst.remove (mev->note(), mev->channel());
+ mst.dump (cerr);
+
+ } else if (mev->is_note_on()) {
+ cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
+ mst.add (mev->note(), mev->channel());
+ source->append_event_unlocked_beats(*i);
+ mst.dump (cerr);
+ } else {
+ cerr << "MIDI other event type\n";
+ source->append_event_unlocked_beats(*i);
+ }
+ }
+ }
+
+ mst.resolve_notes (*source, end_time);
set_percussive(old_percussive);
source->mark_streaming_write_completed();
diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc
index 936ce047ee..eaac23300b 100644
--- a/libs/ardour/midi_region.cc
+++ b/libs/ardour/midi_region.cc
@@ -37,6 +37,7 @@
#include "ardour/dB.h"
#include "ardour/playlist.h"
#include "ardour/midi_source.h"
+#include "ardour/region_factory.h"
#include "ardour/types.h"
#include "ardour/midi_ring_buffer.h"
@@ -68,6 +69,28 @@ MidiRegion::~MidiRegion ()
{
}
+/** Create a new MidiRegion that has its own version of some/all of the Source used by another.
+ */
+boost::shared_ptr<MidiRegion>
+MidiRegion::clone ()
+{
+ BeatsFramesConverter bfc (_session.tempo_map(), _position);
+ double bbegin = bfc.from (_position);
+ double bend = bfc.from (last_frame() + 1);
+
+ boost::shared_ptr<MidiSource> ms = midi_source(0)->clone (bbegin, bend);
+
+ PropertyList plist;
+
+ plist.add (Properties::name, ms->name());
+ plist.add (Properties::whole_file, true);
+ plist.add (Properties::start, 0);
+ plist.add (Properties::length, _length);
+ plist.add (Properties::layer, 0);
+
+ return boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (ms, plist, true));
+}
+
void
MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
{
diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc
index 2b0efd78e1..5e8bda1ea2 100644
--- a/libs/ardour/midi_source.cc
+++ b/libs/ardour/midi_source.cc
@@ -28,6 +28,8 @@
#include <iomanip>
#include <algorithm>
+#include <glibmm/fileutils.h>
+
#include "pbd/xml++.h"
#include "pbd/pthread_utils.h"
#include "pbd/basename.h"
@@ -81,6 +83,7 @@ MidiSource::MidiSource (Session& s, const XMLNode& node)
}
}
+
MidiSource::~MidiSource ()
{
}
@@ -223,44 +226,59 @@ MidiSource::mark_streaming_write_completed ()
_writing = false;
}
+boost::shared_ptr<MidiSource>
+MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
+{
+ string newname = PBD::basename_nosuffix(_name.val());
+ string newpath;
+
+ /* get a new name for the MIDI file we're going to write to
+ */
+
+ do {
+
+ newname = bump_name_once (newname, '-');
+ /* XXX build path safely */
+ newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid";
+
+ } while (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS));
+
+ boost::shared_ptr<MidiSource> newsrc = boost::dynamic_pointer_cast<MidiSource>(
+ SourceFactory::createWritable(DataType::MIDI, _session,
+ newpath, false, _session.frame_rate()));
+
+ newsrc->set_timeline_position(_timeline_position);
+
+ if (_model) {
+ _model->write_to (newsrc, begin, end);
+ } else {
+ error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
+ return boost::shared_ptr<MidiSource>();
+ }
+
+ newsrc->flush_midi();
+
+ /* force a reload of the model if the range is partial */
+
+ if (begin != Evoral::MinMusicalTime || end != Evoral::MaxMusicalTime) {
+ newsrc->load_model (true, true);
+ }
+
+ return newsrc;
+}
+
void
MidiSource::session_saved()
{
flush_midi();
if (_model && _model->edited()) {
- string newname;
- const string basename = PBD::basename_nosuffix(_name.val());
- string::size_type last_dash = basename.find_last_of("-");
- if (last_dash == string::npos || last_dash == basename.find_first_of("-")) {
- newname = basename + "-1";
- } else {
- stringstream ss(basename.substr(last_dash+1));
- unsigned write_count = 0;
- ss >> write_count;
- // cerr << "WRITE COUNT: " << write_count << endl;
- ++write_count; // start at 1
- ss.clear();
- ss << basename.substr(0, last_dash) << "-" << write_count;
- newname = ss.str();
- }
-
- string newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid";
-
- boost::shared_ptr<MidiSource> newsrc = boost::dynamic_pointer_cast<MidiSource>(
- SourceFactory::createWritable(DataType::MIDI, _session,
- newpath, false, _session.frame_rate()));
-
- newsrc->set_timeline_position(_timeline_position);
- _model->write_to(newsrc);
-
- // cyclic dependency here, ugly :(
- newsrc->set_model(_model);
- _model->set_midi_source(newsrc.get());
-
- newsrc->flush_midi();
+ boost::shared_ptr<MidiSource> newsrc = clone ();
- Switched (newsrc); /* EMIT SIGNAL */
+ if (newsrc) {
+ _model->set_midi_source (newsrc.get());
+ Switched (newsrc); /* EMIT SIGNAL */
+ }
}
}
diff --git a/libs/ardour/midi_state_tracker.cc b/libs/ardour/midi_state_tracker.cc
index b2c0086039..da6b8f40b4 100644
--- a/libs/ardour/midi_state_tracker.cc
+++ b/libs/ardour/midi_state_tracker.cc
@@ -20,6 +20,7 @@
#include <iostream>
#include "ardour/event_type_map.h"
#include "ardour/midi_ring_buffer.h"
+#include "ardour/midi_source.h"
#include "ardour/midi_state_tracker.h"
using namespace std;
@@ -133,6 +134,35 @@ MidiStateTracker::resolve_notes (Evoral::EventSink<nframes_t> &dst, nframes64_t
}
void
+MidiStateTracker::resolve_notes (MidiSource& src, Evoral::MusicalTime time)
+{
+ if (!_on) {
+ return;
+ }
+
+ /* NOTE: the src must be locked */
+
+ for (int channel = 0; channel < 16; ++channel) {
+ for (int note = 0; note < 128; ++note) {
+ while (_active_notes[note + 128 * channel]) {
+ Evoral::MIDIEvent<Evoral::MusicalTime> ev ((MIDI_CMD_NOTE_OFF|channel), time, 3, 0, true);
+ ev.set_type (MIDI_CMD_NOTE_OFF);
+ ev.set_channel (channel);
+ ev.set_note (note);
+ ev.set_velocity (0);
+ src.append_event_unlocked_beats (ev);
+ _active_notes[note + 128 * channel]--;
+ cerr << "Resolved " << ev << endl;
+ /* don't stack events up at the same time
+ */
+ time += 1.0/128.0;
+ }
+ }
+ }
+ _on = 0;
+}
+
+void
MidiStateTracker::dump (ostream& o)
{
o << "******\n";
diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc
index a5e662f4d0..7c9ac93c3c 100644
--- a/libs/ardour/playlist.cc
+++ b/libs/ardour/playlist.cc
@@ -1724,6 +1724,8 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
to_check.insert (start);
to_check.insert (end);
+ DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
+
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
/* find all/any regions that span start+end */
@@ -1734,22 +1736,38 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
case OverlapInternal:
covering.push_back (*i);
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
break;
case OverlapStart:
to_check.insert ((*i)->position());
+ if ((*i)->position() != 0) {
+ to_check.insert ((*i)->position()-1);
+ }
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
covering.push_back (*i);
break;
case OverlapEnd:
to_check.insert ((*i)->last_frame());
+ to_check.insert ((*i)->last_frame()+1);
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
covering.push_back (*i);
break;
case OverlapExternal:
covering.push_back (*i);
to_check.insert ((*i)->position());
+ if ((*i)->position() != 0) {
+ to_check.insert ((*i)->position()-1);
+ }
to_check.insert ((*i)->last_frame());
+ to_check.insert ((*i)->last_frame()+1);
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
break;
}
@@ -1767,6 +1785,7 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
if (covering.size() == 1) {
rlist->push_back (covering.front());
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
} else {
@@ -1775,11 +1794,21 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
here.clear ();
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
+
for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
if ((*x)->covers (*t)) {
here.push_back (*x);
- }
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
+ (*x)->name(),
+ (*t)));
+ } else {
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
+ (*x)->name(),
+ (*t)));
+ }
+
}
RegionSortByLayer cmp;
@@ -1794,7 +1823,8 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
if ((*c)->opaque()) {
/* the other regions at this position are hidden by this one */
-
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
+ (*c)->name()));
break;
}
}
@@ -1812,6 +1842,8 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
}
}
+ DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
+
return rlist;
}
@@ -2311,7 +2343,7 @@ Playlist::bump_name (string name, Session &session)
string newname = name;
do {
- newname = bump_name_once (newname);
+ newname = bump_name_once (newname, '.');
} while (session.playlists->by_name (newname)!=NULL);
return newname;
diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc
index c0edb1a996..86b4b89d16 100644
--- a/libs/ardour/route.cc
+++ b/libs/ardour/route.cc
@@ -272,7 +272,7 @@ Route::ensure_track_or_route_name(string name, Session &session)
string newname = name;
while (!session.io_name_is_legal (newname)) {
- newname = bump_name_once (newname);
+ newname = bump_name_once (newname, '.');
}
return newname;
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 2739302140..a78bfbfdd1 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -728,8 +728,9 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
/* tell sources we're saving first, in case they write out to a new file
* which should be saved with the state rather than the old one */
- for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i)
+ for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
i->second->session_saved();
+ }
tree.set_root (&get_state());
diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc
index af90c935c9..2bfe16222d 100644
--- a/libs/ardour/utils.cc
+++ b/libs/ardour/utils.cc
@@ -72,17 +72,19 @@ legalize_for_path (ustring str)
return legal;
}
-string bump_name_once(std::string name)
+string
+bump_name_once (const std::string& name, char delimiter)
{
- string::size_type period;
+ string::size_type delim;
string newname;
- if ((period = name.find_last_of ('.')) == string::npos) {
+ if ((delim = name.find_last_of (delimiter)) == string::npos) {
newname = name;
- newname += ".1";
+ newname += delimiter;
+ newname += "1";
} else {
int isnumber = 1;
- const char *last_element = name.c_str() + period + 1;
+ const char *last_element = name.c_str() + delim + 1;
for (size_t i = 0; i < strlen(last_element); i++) {
if (!isdigit(last_element[i])) {
isnumber = 0;
@@ -91,18 +93,19 @@ string bump_name_once(std::string name)
}
errno = 0;
- long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
+ long int version = strtol (name.c_str()+delim+1, (char **)NULL, 10);
if (isnumber == 0 || errno != 0) {
// last_element is not a number, or is too large
newname = name;
- newname += ".1";
+ newname += delimiter;
+ newname += "1";
} else {
char buf[32];
snprintf (buf, sizeof(buf), "%ld", version+1);
- newname = name.substr (0, period+1);
+ newname = name.substr (0, delim+1);
newname += buf;
}
}
diff --git a/libs/evoral/evoral/ControlSet.hpp b/libs/evoral/evoral/ControlSet.hpp
index 39c3eba344..f0c6bd0807 100644
--- a/libs/evoral/evoral/ControlSet.hpp
+++ b/libs/evoral/evoral/ControlSet.hpp
@@ -36,6 +36,7 @@ class ControlEvent;
class ControlSet : public boost::noncopyable {
public:
ControlSet();
+ ControlSet (const ControlSet&);
virtual ~ControlSet() {}
virtual boost::shared_ptr<Evoral::Control>
diff --git a/libs/evoral/evoral/MIDIEvent.hpp b/libs/evoral/evoral/MIDIEvent.hpp
index c06a323d73..359d640fe5 100644
--- a/libs/evoral/evoral/MIDIEvent.hpp
+++ b/libs/evoral/evoral/MIDIEvent.hpp
@@ -68,6 +68,7 @@ struct MIDIEvent : public Event<Time> {
inline bool is_aftertouch() const { return (type() == MIDI_CMD_NOTE_PRESSURE); }
inline bool is_channel_pressure() const { return (type() == MIDI_CMD_CHANNEL_PRESSURE); }
inline uint8_t note() const { return (this->_buf[1]); }
+ inline void set_note(uint8_t n) { this->_buf[1] = n; }
inline uint8_t velocity() const { return (this->_buf[2]); }
inline void set_velocity(uint8_t value) { this->_buf[2] = value; }
inline void scale_velocity(float factor) {
diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp
index 8c0eab1be9..3aafb15312 100644
--- a/libs/evoral/evoral/Sequence.hpp
+++ b/libs/evoral/evoral/Sequence.hpp
@@ -61,6 +61,7 @@ template<typename Time>
class Sequence : virtual public ControlSet {
public:
Sequence(const TypeMap& type_map);
+ Sequence(const Sequence<Time>& other);
protected:
struct WriteLockImpl {
diff --git a/libs/evoral/evoral/types.hpp b/libs/evoral/evoral/types.hpp
index 2ba5ba86cb..a2cc814c69 100644
--- a/libs/evoral/evoral/types.hpp
+++ b/libs/evoral/evoral/types.hpp
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <list>
#include <cmath>
+#include <cfloat>
namespace Evoral {
@@ -30,6 +31,8 @@ typedef uint32_t FrameTime;
/** Musical time: beats relative to some defined origin */
typedef double MusicalTime;
+const MusicalTime MaxMusicalTime = DBL_MAX;
+const MusicalTime MinMusicalTime = DBL_MIN;
static inline bool musical_time_equal (MusicalTime a, MusicalTime b) {
/* acceptable tolerance is 1 tick. Nice if there was no magic number here */
diff --git a/libs/evoral/src/ControlSet.cpp b/libs/evoral/src/ControlSet.cpp
index 29d3b40344..e19acf7689 100644
--- a/libs/evoral/src/ControlSet.cpp
+++ b/libs/evoral/src/ControlSet.cpp
@@ -31,6 +31,12 @@ ControlSet::ControlSet()
{
}
+ControlSet::ControlSet (const ControlSet& other)
+ : noncopyable ()
+{
+ /* derived class must copy controls */
+}
+
void
ControlSet::add_control(boost::shared_ptr<Control> ac)
{
diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp
index 93eccb6cce..281aec514b 100644
--- a/libs/evoral/src/Sequence.cpp
+++ b/libs/evoral/src/Sequence.cpp
@@ -398,6 +398,32 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
assert( ! _end_iter._lock);
}
+template<typename Time>
+Sequence<Time>::Sequence(const Sequence<Time>& other)
+ : ControlSet (other)
+ , _edited(false)
+ , _type_map(other._type_map)
+ , _writing(false)
+ , _end_iter(*this, DBL_MAX)
+ , _percussive(other._percussive)
+ , _lowest_note(other._lowest_note)
+ , _highest_note(other._highest_note)
+{
+ for (typename Notes::const_iterator i = other._notes.begin(); i != other._notes.end(); ++i) {
+ boost::shared_ptr<Note<Time> > n (new Note<Time> (**i));
+ _notes.insert (n);
+ }
+
+ for (typename SysExes::const_iterator i = other._sysexes.begin(); i != other._sysexes.end(); ++i) {
+ boost::shared_ptr<Event<Time> > n (new Event<Time> (**i, true));
+ _sysexes.push_back (n);
+ }
+
+ DUMP(format("Sequence copied: %1%\n") % this);
+ assert(_end_iter._is_end);
+ assert(! _end_iter._lock);
+}
+
/** Write the controller event pointed to by \a iter to \a ev.
* The buffer of \a ev will be allocated or resized as necessary.
* The event_type of \a ev should be set to the expected output type.