diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2010-05-19 03:03:28 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2010-05-19 03:03:28 +0000 |
commit | e258b2622a4386b405c2216d79b34887c3ed55bf (patch) | |
tree | c2abdacc5a31e9d572257050256c704b41fb46f5 | |
parent | c25c7598c134af88bb85b5690aabc35472c77adf (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
31 files changed, 332 insertions, 64 deletions
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 9b71086cc6..e8c39faade 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -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()); } diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index e60ae99c9a..48798543a9 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -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); diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 54ee1288c2..9628928004 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -4649,6 +4649,47 @@ Editor::apply_midi_note_edit_op (MidiOperator& op) } 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 () { if (!_session) { diff --git a/gtk2_ardour/midi_cut_buffer.cc b/gtk2_ardour/midi_cut_buffer.cc index 9c1c84ede4..1bc6ab212a 100644 --- a/gtk2_ardour/midi_cut_buffer.cc +++ b/gtk2_ardour/midi_cut_buffer.cc @@ -21,7 +21,7 @@ using namespace ARDOUR; MidiCutBuffer::MidiCutBuffer (Session* s) - : AutomatableSequence<MidiModel::TimeType> (*s, 0) + : AutomatableSequence<MidiModel::TimeType> (*s) , _origin (0) { 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. |