X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fmidi_model.cc;h=47bc14852c1cf1828521f98fbc57415bebff3938;hb=b0e41486f3b68d2d5f803a761dc49a42c4599339;hp=0b38ad06a8306a2c395b4afbce2b912aba866d98;hpb=74ff55f835656f617f1c508508cda49ac5836c91;p=ardour.git diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index 0b38ad06a8..47bc14852c 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -1,22 +1,22 @@ /* - Copyright (C) 2007 Paul Davis - Written by Dave Robillard, 2007 + Copyright (C) 2007 Paul Davis + Author: Dave Robillard - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ +*/ #define __STDC_LIMIT_MACROS 1 @@ -24,38 +24,52 @@ #include #include #include -#include -#include -#include +#include "pbd/error.h" +#include "pbd/enumwriter.h" +#include "midi++/events.h" -#include -#include -#include -#include +#include "ardour/midi_model.h" +#include "ardour/midi_source.h" +#include "ardour/smf_source.h" +#include "ardour/types.h" +#include "ardour/session.h" using namespace std; using namespace ARDOUR; using namespace PBD; -MidiModel::MidiModel(MidiSource *s, size_t size) +MidiModel::MidiModel(MidiSource* s, size_t size) : AutomatableSequence(s->session(), size) , _midi_source(s) { - //cerr << "MidiModel \"" << s->name() << "\" constructed: " << this << endl; } -/** Start a new command. +/** Start a new Delta command. * * This has no side-effects on the model or Session, the returned command * can be held on to for as long as the caller wishes, or discarded without * formality, until apply_command is called and ownership is taken. */ -MidiModel::DeltaCommand* MidiModel::new_delta_command(const string name) +MidiModel::DeltaCommand* +MidiModel::new_delta_command(const string name) { DeltaCommand* cmd = new DeltaCommand(_midi_source->model(), name); return cmd; } +/** Start a new Diff command. + * + * This has no side-effects on the model or Session, the returned command + * can be held on to for as long as the caller wishes, or discarded without + * formality, until apply_command is called and ownership is taken. + */ +MidiModel::DiffCommand* +MidiModel::new_diff_command(const string name) +{ + DiffCommand* cmd = new DiffCommand(_midi_source->model(), name); + return cmd; +} + /** Apply a command. * * Ownership of cmd is taken, it must not be deleted by the caller. @@ -70,28 +84,40 @@ MidiModel::apply_command(Session& session, Command* cmd) set_edited(true); } +/** Apply a command as part of a larger reversible transaction + * + * Ownership of cmd is taken, it must not be deleted by the caller. + * The command will constitute one item on the undo stack. + */ +void +MidiModel::apply_command_as_subcommand(Session& session, Command* cmd) +{ + (*cmd)(); + session.add_command(cmd); + set_edited(true); +} + // DeltaCommand -MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr m, - const std::string& name) +MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr m, const std::string& name) : Command(name) , _model(m) , _name(name) { + assert(_model); } -MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr m, - const XMLNode& node) +MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr m, const XMLNode& node) : _model(m) { - set_state(node); + assert(_model); + set_state(node, Stateful::loading_state_version); } void MidiModel::DeltaCommand::add(const boost::shared_ptr< Evoral::Note > note) { - //cerr << "MEC: apply" << endl; _removed_notes.remove(note); _added_notes.push_back(note); } @@ -99,7 +125,6 @@ MidiModel::DeltaCommand::add(const boost::shared_ptr< Evoral::Note > n void MidiModel::DeltaCommand::remove(const boost::shared_ptr< Evoral::Note > note) { - //cerr << "MEC: remove" << endl; _added_notes.remove(note); _removed_notes.push_back(note); } @@ -109,23 +134,18 @@ MidiModel::DeltaCommand::operator()() { // This could be made much faster by using a priority_queue for added and // removed notes (or sort here), and doing a single iteration over _model - - _model->write_lock(); - // Store the current seek position so we can restore the read iterator - // after modifying the contents of the model - const double read_time = _model->read_time(); + MidiModel::WriteLock lock(_model->edit_lock()); - for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) + for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) { _model->add_note_unlocked(*i); + } - for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) + for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) { _model->remove_note_unlocked(*i); + } - _model->write_unlock(); - // FIXME: race? - _model->read_seek(read_time); // restore read position - + lock.reset(); _model->ContentsChanged(); /* EMIT SIGNAL */ } @@ -134,30 +154,25 @@ MidiModel::DeltaCommand::undo() { // This could be made much faster by using a priority_queue for added and // removed notes (or sort here), and doing a single iteration over _model - - _model->write_lock(); - // Store the current seek position so we can restore the read iterator - // after modifying the contents of the model - const double read_time = _model->read_time(); + MidiModel::WriteLock lock(_model->edit_lock());; - for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) + for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) { _model->remove_note_unlocked(*i); + } - for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) + for (NoteList::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i) { _model->add_note_unlocked(*i); + } - _model->write_unlock(); - // FIXME: race? - _model->read_seek(read_time); // restore read position - + lock.reset(); _model->ContentsChanged(); /* EMIT SIGNAL */ } XMLNode& MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note > note) { - XMLNode *xml_note = new XMLNode("note"); + XMLNode* xml_note = new XMLNode("note"); ostringstream note_str(ios::ate); note_str << int(note->note()); xml_note->add_property("note", note_str.str()); @@ -181,7 +196,7 @@ MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr< Evoral::Note