MIDI region forking, plus Playlist::regions_to_read() fix forward ported from 2.X...
authorPaul Davis <paul@linuxaudiosystems.com>
Wed, 19 May 2010 03:03:28 +0000 (03:03 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Wed, 19 May 2010 03:03:28 +0000 (03:03 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@7118 d708f5d6-7413-0410-9779-e7cbd77b26cf

31 files changed:
gtk2_ardour/editor.cc
gtk2_ardour/editor.h
gtk2_ardour/editor_ops.cc
gtk2_ardour/midi_cut_buffer.cc
libs/ardour/ardour/audioregion.h
libs/ardour/ardour/automatable.h
libs/ardour/ardour/automatable_sequence.h
libs/ardour/ardour/debug.h
libs/ardour/ardour/midi_model.h
libs/ardour/ardour/midi_region.h
libs/ardour/ardour/midi_source.h
libs/ardour/ardour/midi_state_tracker.h
libs/ardour/ardour/utils.h
libs/ardour/audio_playlist.cc
libs/ardour/audioregion.cc
libs/ardour/automatable.cc
libs/ardour/debug.cc
libs/ardour/midi_model.cc
libs/ardour/midi_region.cc
libs/ardour/midi_source.cc
libs/ardour/midi_state_tracker.cc
libs/ardour/playlist.cc
libs/ardour/route.cc
libs/ardour/session_state.cc
libs/ardour/utils.cc
libs/evoral/evoral/ControlSet.hpp
libs/evoral/evoral/MIDIEvent.hpp
libs/evoral/evoral/Sequence.hpp
libs/evoral/evoral/types.hpp
libs/evoral/src/ControlSet.cpp
libs/evoral/src/Sequence.cpp

index 9b71086cc6a164d6af938e264758a34a31450393..e8c39faade70e5c0c8525d91228be054923d9c8d 100644 (file)
@@ -1792,6 +1792,7 @@ Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> regi
 
        } else if (mr) {
                items.push_back (MenuElem (_("Quantize"), sigc::mem_fun(*this, &Editor::quantize_region)));
+               items.push_back (MenuElem (_("Fork"), sigc::mem_fun(*this, &Editor::fork_region)));
                items.push_back (SeparatorElem());
        }
 
index e60ae99c9aab537c28098ceecbcbc4a3f7d79d4a..48798543a9fa215ae51590d15796b0f57583e754 100644 (file)
@@ -1075,6 +1075,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
        void reset_region_scale_amplitude ();
        void adjust_region_scale_amplitude (bool up);
        void quantize_region ();
+       void fork_region ();
 
        void do_insert_time ();
        void insert_time (nframes64_t, nframes64_t, Editing::InsertTimeOption, bool, bool, bool);
index 54ee1288c2ad2120a3fb1e0e2ec1ba4f1081ff98..96289280046f52d8770891ff9790ac5e2c988dbf 100644 (file)
@@ -4648,6 +4648,47 @@ Editor::apply_midi_note_edit_op (MidiOperator& op)
        rs.clear ();
 }
 
+void
+Editor::fork_region ()
+{
+       RegionSelection rs;
+
+       get_regions_for_action (rs);
+
+       if (rs.empty()) {
+               return;
+       }
+
+       begin_reversible_command (_("Fork Region(s)"));
+
+       track_canvas->get_window()->set_cursor (*wait_cursor);
+       gdk_flush ();
+
+       for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
+               RegionSelection::iterator tmp = r;
+               ++tmp;
+
+               MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
+
+               if (mrv) {
+                       boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
+                        boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
+                        
+                        playlist->clear_history ();
+                        cerr << "Replace region with " << newregion->name() << endl;
+                        playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
+                        _session->add_command(new StatefulDiffCommand (playlist));
+               }
+
+               r = tmp;
+       }
+
+       commit_reversible_command ();
+       rs.clear ();
+
+       track_canvas->get_window()->set_cursor (*current_canvas_cursor);
+}
+
 void
 Editor::quantize_region ()
 {
index 9c1c84ede4d76cbdc963f5f5ab2d67c496b236a3..1bc6ab212aa8836ada684275aac0465b1e6583c4 100644 (file)
@@ -21,7 +21,7 @@
 using namespace ARDOUR;
 
 MidiCutBuffer::MidiCutBuffer (Session* s)
-       : AutomatableSequence<MidiModel::TimeType> (*s, 0)
+       : AutomatableSequence<MidiModel::TimeType> (*s)
        , _origin (0)
 {
        
index f9c13b2bbe1172c7e327a941ccec6f3f69e866c9..0a3e53039e13f126e4b80d7bd861e229e8d81b06 100644 (file)
@@ -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,
index 3236d816c315447725ae0e097ddf9d5648f8580a..9b83705b0a02e28a124a80b28c44c3f79aab2e05 100644 (file)
@@ -42,6 +42,7 @@ class Automatable : virtual public Evoral::ControlSet
 {
 public:
        Automatable(Session&);
+        Automatable (const Automatable& other);
        Automatable();
 
        virtual ~Automatable() {}
index 88e1733c1b7c057add86bf592a2192638695db52..730ea33a7cf5f600e22e548ecf6180faac48968f 100644 (file)
@@ -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
index 57b66a87937428669f0b7590976f89577f45497e..1cd8854feaa6e92f450d617800fc8b935dd4d56c 100644 (file)
@@ -46,6 +46,7 @@ namespace PBD {
                 extern uint64_t MidiClock;
                 extern uint64_t Monitor;
                 extern uint64_t Solo;
+                extern uint64_t AudioPlayback;
        }
 }
 
index fcd285f98ced422cf4d37b4b118f53182df46e7f..8d949cadbb6f46922b5442fe4f97d16501681f64 100644 (file)
@@ -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
index 671e4fca432e7db8b49e49e35c40c415e9080f51..568638ed2138560b6de6e883ae3f546924d51cf5 100644 (file)
@@ -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 */
index 7b867c70fd0a41eb7be9e9ec30ef6d42532a07dc..2484d3575a21f06e46e7d5535b47061be4a80d25 100644 (file)
@@ -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
index 76a669838aa6d9565681e13037a99f86c5e3d00f..5c65c1f018e52e3d31199f380ba39d8b306575a0 100644 (file)
@@ -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);
index e68f7a01df5bc012bc6f81e1fd661714051f4ad6..e7e8f2b820deb80be52e66d68fb66e5c8e6bd646 100644 (file)
@@ -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);
 
index 65b05b7c92962ac0c624042d8c2054327a28cae9..bd09af4e1f8d11b56df3ee485f78288d53cc17d7 100644 (file)
@@ -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();
index c8e742555c85d2f4d8d2d31d5e89daa277148544..0ef79b7018807e234b0c64201c80f009f4893d77 100644 (file)
@@ -317,12 +317,6 @@ AudioRegion::read (Sample* buf, framepos_t timeline_position, framecnt_t cnt, in
        return _read_at (_sources, _length, buf, 0, 0, _position + timeline_position, cnt, channel, 0, 0, ReadOps (0));
 }
 
-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,
index 1ef39a61f0df82dfd81f9481b214c1c336cd9641..3d705b3ea78a5b212f628668d6ccca4bd280d570 100644 (file)
@@ -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)
 {
index 268ec5d0336f83120b7b28d8ea4064098677d66b..419579b977dfeeaa7fc824c3da400d2b32d56890 100644 (file)
@@ -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");
 
index ac3360ea2b0e77776f38a43ecfea767a56fae7bc..88ef60e8e56ccd7d78ba43e4c92a71d3089c1919 100644 (file)
@@ -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();
index 936ce047eec35f2620641a37f2a3f0bbc9e37bf3..eaac23300b96ba55ffa17be8bd5c8676839cfe38 100644 (file)
@@ -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)
 {
index 2b0efd78e165d7a02f40433722a72e241e3c813c..5e8bda1ea29ce2f190899526a859924fd5dd457b 100644 (file)
@@ -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 */
+                }
        }
 }
 
index b2c008603958250994eae5e3a6857ed13cf23fd2..da6b8f40b44a0a070a38595a3b4723ea05dd6027 100644 (file)
@@ -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;
@@ -132,6 +133,35 @@ MidiStateTracker::resolve_notes (Evoral::EventSink<nframes_t> &dst, nframes64_t
        _on = 0;
 }
 
+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)
 {
index a5e662f4d024790de6d45b2c4ce61d664e6a22fb..7c9ac93c3ca0dd70e52aec7a7f1e3202f91f63ee 100644 (file)
@@ -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;
index c0edb1a996c4b40234be723f62eba1758fb3f97b..86b4b89d167cb04055efcca75e0fb67a05981c4e 100644 (file)
@@ -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;
index 2739302140ccc693643eaf5aa911dfd376369ef8..a78bfbfdd1c998a130a6c4c2b68ebdaf13a27943 100644 (file)
@@ -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());
 
index af90c935c918cce68c0b54f4cf78354d2bbc0a16..2bfe16222d1ca5c8ec9ce71aab45b39291e396f6 100644 (file)
@@ -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;
                }
        }
index 39c3eba3449f99f3ddc1d7e0cef60bb1c8490848..f0c6bd0807e2c564f72e4a9e30e8f936b8d19530 100644 (file)
@@ -36,6 +36,7 @@ class ControlEvent;
 class ControlSet : public boost::noncopyable {
 public:
        ControlSet();
+        ControlSet (const ControlSet&);
        virtual ~ControlSet() {}
 
        virtual boost::shared_ptr<Evoral::Control>
index c06a323d73ed8c73380f492c190c19de8bca4509..359d640fe598182c29813550839ea0a67dd23095 100644 (file)
@@ -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)  {
index 8c0eab1be9c8fb6a9a89d56acad156297fdd729c..3aafb15312e718222b2a750022882518a07743d5 100644 (file)
@@ -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 {
index 2ba5ba86cbe28ecc7f71f6db2914b6ba032663e9..a2cc814c69c4e09c29f8239c9ff395f42ad1cb31 100644 (file)
@@ -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 */
index 29d3b40344d9e991c650e77a5436a78ae068622b..e19acf7689fa8447ea7f731493b87121e3c7d02f 100644 (file)
@@ -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)
 {
index 93eccb6cce000467427d6a691207ab02dbcb3ecc..281aec514bacb413b57c4393711e869ef1581ae6 100644 (file)
@@ -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.