Midi pencil undo (not yet serializable).

Formatting fixes for session.h (ie kill more of those damned 8 space expanded tabs).


git-svn-id: svn://localhost/ardour2/trunk@2135 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2007-07-17 01:48:42 +00:00
parent 37c74810d2
commit f542fa693c
7 changed files with 210 additions and 54 deletions

View file

@ -1,5 +1,6 @@
/* /*
Copyright (C) 2001-2006 Paul Davis Copyright (C) 2001-2007 Paul Davis
Author: Dave Robillard
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -24,6 +25,8 @@
#include <gtkmm2ext/gtk_ui.h> #include <gtkmm2ext/gtk_ui.h>
#include <sigc++/signal.h>
#include <ardour/playlist.h> #include <ardour/playlist.h>
#include <ardour/tempo.h> #include <ardour/tempo.h>
#include <ardour/midi_region.h> #include <ardour/midi_region.h>
@ -96,6 +99,9 @@ MidiRegionView::init (Gdk::Color& basic_color, bool wfd)
display_events(); display_events();
} }
midi_region()->midi_source(0)->model()->ContentsChanged.connect(sigc::mem_fun(
this, &MidiRegionView::redisplay_model));
group->signal_event().connect (mem_fun (this, &MidiRegionView::canvas_event)); group->signal_event().connect (mem_fun (this, &MidiRegionView::canvas_event));
} }
@ -127,13 +133,15 @@ MidiRegionView::canvas_event(GdkEvent* ev)
const Tempo& t = trackview.session().tempo_map().tempo_at(stamp); const Tempo& t = trackview.session().tempo_map().tempo_at(stamp);
double dur = m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar(); double dur = m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar();
MidiModel* model = midi_region()->midi_source(0)->model();
// Add a 1 beat long note (for now) // Add a 1 beat long note (for now)
const MidiModel::Note new_note(stamp, dur, (uint8_t)note, 0x40); const MidiModel::Note new_note(stamp, dur, (uint8_t)note, 0x40);
MidiModel::Notes& notes = midi_region()->midi_source(0)->model()->notes(); model->begin_command();
MidiModel::Notes::iterator i = upper_bound(notes.begin(), notes.end(), new_note, model->add_note(new_note);
MidiModel::NoteTimeComparator()); model->finish_command();
notes.insert(i, new_note);
view->update_bounds(new_note.note); view->update_bounds(new_note.note);
add_note(new_note); add_note(new_note);
@ -144,6 +152,14 @@ MidiRegionView::canvas_event(GdkEvent* ev)
} }
void
MidiRegionView::redisplay_model()
{
clear_events();
display_events();
}
void void
MidiRegionView::clear_events() MidiRegionView::clear_events()
{ {

View file

@ -23,7 +23,6 @@
#include <libgnomecanvasmm.h> #include <libgnomecanvasmm.h>
#include <libgnomecanvasmm/polygon.h> #include <libgnomecanvasmm/polygon.h>
#include <sigc++/signal.h>
#include <ardour/midi_region.h> #include <ardour/midi_region.h>
#include <ardour/midi_model.h> #include <ardour/midi_model.h>
#include <ardour/types.h> #include <ardour/types.h>
@ -94,6 +93,7 @@ class MidiRegionView : public RegionView
private: private:
void redisplay_model();
void display_events(); void display_events();
void clear_events(); void clear_events();

View file

@ -22,11 +22,15 @@
#define __ardour_midi_model_h__ #define __ardour_midi_model_h__
#include <boost/utility.hpp> #include <boost/utility.hpp>
#include <pbd/command.h>
#include <ardour/types.h> #include <ardour/types.h>
#include <ardour/midi_buffer.h> #include <ardour/midi_buffer.h>
namespace ARDOUR { namespace ARDOUR {
class Session;
/** This is a slightly higher level (than MidiBuffer) model of MIDI note data. /** This is a slightly higher level (than MidiBuffer) model of MIDI note data.
* Currently it only represents note data, which is represented as complete * Currently it only represents note data, which is represented as complete
* note events (ie with a start time and a duration) rather than separate * note events (ie with a start time and a duration) rather than separate
@ -39,13 +43,16 @@ public:
Note(double s=0, double d=0, uint8_t n=0, uint8_t v=0) Note(double s=0, double d=0, uint8_t n=0, uint8_t v=0)
: start(s), duration(d), note(n), velocity(v) {} : start(s), duration(d), note(n), velocity(v) {}
inline bool operator==(const Note& other)
{ return start == other.start && note == other.note; }
double start; double start;
double duration; double duration;
uint8_t note; uint8_t note;
uint8_t velocity; uint8_t velocity;
}; };
MidiModel(size_t size=0); MidiModel(Session& s, size_t size=0);
void clear() { _notes.clear(); } void clear() { _notes.clear(); }
@ -73,13 +80,47 @@ public:
inline Notes& notes() { return _notes; } inline Notes& notes() { return _notes; }
inline const Notes& notes() const { return _notes; } inline const Notes& notes() const { return _notes; }
void begin_command();
Command* current_command() { return _command; }
void finish_command();
// Commands
void add_note(const Note& note);
void remove_note(const Note& note);
sigc::signal<void> ContentsChanged;
private: private:
class MidiEditCommand : public Command
{
public:
MidiEditCommand (MidiModel& m) : _model(m) {}
//MidiEditCommand (MidiModel&, const XMLNode& node);
void operator()();
void undo();
/*int set_state (const XMLNode&);
XMLNode& get_state ();*/
void add_note(const Note& note);
void remove_note(const Note& note);
private:
MidiModel& _model;
std::list<Note> _added_notes;
std::list<Note> _removed_notes;
};
void append_note_on(double time, uint8_t note, uint8_t velocity); void append_note_on(double time, uint8_t note, uint8_t velocity);
void append_note_off(double time, uint8_t note); void append_note_off(double time, uint8_t note);
Session& _session;
Notes _notes; Notes _notes;
Notes _write_notes; Notes _write_notes;
MidiEditCommand* _command; ///< In-progress command
}; };
} /* namespace ARDOUR */ } /* namespace ARDOUR */

View file

@ -487,7 +487,7 @@ class Session : public PBD::StatefulDestructible
void resort_routes (); void resort_routes ();
void resort_routes_using (boost::shared_ptr<RouteList>); void resort_routes_using (boost::shared_ptr<RouteList>);
void set_remote_control_ids(); void set_remote_control_ids();
AudioEngine &engine() { return _engine; }; AudioEngine &engine() { return _engine; };
@ -538,7 +538,7 @@ class Session : public PBD::StatefulDestructible
void request_slave_source (SlaveSource); void request_slave_source (SlaveSource);
bool synced_to_jack() const { return Config->get_slave_source() == JACK; } bool synced_to_jack() const { return Config->get_slave_source() == JACK; }
float transport_speed() const { return _transport_speed; } float transport_speed() const { return _transport_speed; }
bool transport_stopped() const { return _transport_speed == 0.0f; } bool transport_stopped() const { return _transport_speed == 0.0f; }
bool transport_rolling() const { return _transport_speed != 0.0f; } bool transport_rolling() const { return _transport_speed != 0.0f; }
@ -595,8 +595,8 @@ class Session : public PBD::StatefulDestructible
void remove_source (boost::weak_ptr<Source>); void remove_source (boost::weak_ptr<Source>);
struct cleanup_report { struct cleanup_report {
vector<string> paths; vector<string> paths;
int64_t space; int64_t space;
}; };
int cleanup_sources (cleanup_report&); int cleanup_sources (cleanup_report&);
@ -673,8 +673,9 @@ class Session : public PBD::StatefulDestructible
/* flattening stuff */ /* flattening stuff */
int write_one_audio_track (AudioTrack&, nframes_t start, nframes_t cnt, bool overwrite, vector<boost::shared_ptr<Source> >&, int write_one_audio_track (AudioTrack&, nframes_t start, nframes_t cnt, bool overwrite,
InterThreadInfo& wot); vector<boost::shared_ptr<Source> >&, InterThreadInfo& wot);
int freeze (InterThreadInfo&); int freeze (InterThreadInfo&);
/* session-wide solo/mute/rec-enable */ /* session-wide solo/mute/rec-enable */
@ -803,8 +804,8 @@ class Session : public PBD::StatefulDestructible
}; };
class GlobalSoloStateCommand : public GlobalRouteStateCommand class GlobalSoloStateCommand : public GlobalRouteStateCommand
{ {
public: public:
GlobalSoloStateCommand (Session &, void *src); GlobalSoloStateCommand (Session &, void *src);
GlobalSoloStateCommand (Session&, const XMLNode&); GlobalSoloStateCommand (Session&, const XMLNode&);
@ -812,10 +813,10 @@ class Session : public PBD::StatefulDestructible
void undo(); void undo();
XMLNode &get_state(); XMLNode &get_state();
void mark(); void mark();
}; };
class GlobalMuteStateCommand : public GlobalRouteStateCommand class GlobalMuteStateCommand : public GlobalRouteStateCommand
{ {
public: public:
GlobalMuteStateCommand(Session &, void *src); GlobalMuteStateCommand(Session &, void *src);
GlobalMuteStateCommand (Session&, const XMLNode&); GlobalMuteStateCommand (Session&, const XMLNode&);
@ -823,10 +824,10 @@ class Session : public PBD::StatefulDestructible
void undo(); void undo();
XMLNode &get_state(); XMLNode &get_state();
void mark(); void mark();
}; };
class GlobalRecordEnableStateCommand : public GlobalRouteStateCommand class GlobalRecordEnableStateCommand : public GlobalRouteStateCommand
{ {
public: public:
GlobalRecordEnableStateCommand(Session &, void *src); GlobalRecordEnableStateCommand(Session &, void *src);
GlobalRecordEnableStateCommand (Session&, const XMLNode&); GlobalRecordEnableStateCommand (Session&, const XMLNode&);
@ -834,10 +835,10 @@ class Session : public PBD::StatefulDestructible
void undo(); void undo();
XMLNode &get_state(); XMLNode &get_state();
void mark(); void mark();
}; };
class GlobalMeteringStateCommand : public Command class GlobalMeteringStateCommand : public Command
{ {
public: public:
GlobalMeteringStateCommand(Session &, void *src); GlobalMeteringStateCommand(Session &, void *src);
GlobalMeteringStateCommand (Session&, const XMLNode&); GlobalMeteringStateCommand (Session&, const XMLNode&);
@ -852,7 +853,7 @@ class Session : public PBD::StatefulDestructible
void* src; void* src;
GlobalRouteMeterState before; GlobalRouteMeterState before;
GlobalRouteMeterState after; GlobalRouteMeterState after;
}; };
/* clicking */ /* clicking */
@ -861,14 +862,14 @@ class Session : public PBD::StatefulDestructible
/* tempo FX */ /* tempo FX */
struct TimeStretchRequest { struct TimeStretchRequest {
boost::shared_ptr<ARDOUR::AudioRegion> region; boost::shared_ptr<ARDOUR::AudioRegion> region;
float fraction; /* session: read ; GUI: write */ float fraction; /* session: read ; GUI: write */
float progress; /* session: write ; GUI: read */ float progress; /* session: write ; GUI: read */
bool running; /* read/write */ bool running; /* read/write */
bool quick_seek; /* GUI: write */ bool quick_seek; /* GUI: write */
bool antialias; /* GUI: write */ bool antialias; /* GUI: write */
TimeStretchRequest () {} TimeStretchRequest () {}
}; };
boost::shared_ptr<AudioRegion> tempoize_region (TimeStretchRequest&); boost::shared_ptr<AudioRegion> tempoize_region (TimeStretchRequest&);
@ -1088,9 +1089,9 @@ class Session : public PBD::StatefulDestructible
bool session_midi_feedback; bool session_midi_feedback;
bool play_loop; bool play_loop;
bool loop_changing; bool loop_changing;
nframes_t last_loopend; nframes_t last_loopend;
boost::scoped_ptr<SessionDirectory> _session_dir; boost::scoped_ptr<SessionDirectory> _session_dir;
RingBuffer<Event*> pending_events; RingBuffer<Event*> pending_events;
@ -1110,11 +1111,11 @@ class Session : public PBD::StatefulDestructible
int load_state (string snapshot_name); int load_state (string snapshot_name);
bool save_config_options_predicate (ConfigVariableBase::Owner owner) const; bool save_config_options_predicate (ConfigVariableBase::Owner owner) const;
nframes_t _last_roll_location; nframes_t _last_roll_location;
nframes_t _last_record_location; nframes_t _last_record_location;
bool pending_locate_roll;
nframes_t pending_locate_frame;
bool pending_locate_roll;
nframes_t pending_locate_frame;
bool pending_locate_flush; bool pending_locate_flush;
bool pending_abort; bool pending_abort;
bool pending_auto_loop; bool pending_auto_loop;
@ -1123,7 +1124,7 @@ class Session : public PBD::StatefulDestructible
float* butler_gain_buffer; float* butler_gain_buffer;
pthread_t butler_thread; pthread_t butler_thread;
Glib::Mutex butler_request_lock; Glib::Mutex butler_request_lock;
Glib::Cond butler_paused; Glib::Cond butler_paused;
bool butler_should_run; bool butler_should_run;
mutable gint butler_should_do_transport_work; mutable gint butler_should_do_transport_work;
int butler_request_pipe[2]; int butler_request_pipe[2];
@ -1512,13 +1513,13 @@ class Session : public PBD::StatefulDestructible
/* INSERT AND SEND MANAGEMENT */ /* INSERT AND SEND MANAGEMENT */
list<PortInsert *> _port_inserts; list<PortInsert *> _port_inserts;
list<PluginInsert *> _plugin_inserts; list<PluginInsert *> _plugin_inserts;
list<Send *> _sends; list<Send *> _sends;
boost::dynamic_bitset<uint32_t> send_bitset; boost::dynamic_bitset<uint32_t> send_bitset;
boost::dynamic_bitset<uint32_t> insert_bitset; boost::dynamic_bitset<uint32_t> insert_bitset;
uint32_t send_cnt; uint32_t send_cnt;
uint32_t insert_cnt; uint32_t insert_cnt;
void add_processor (Processor *); void add_processor (Processor *);
@ -1605,7 +1606,7 @@ class Session : public PBD::StatefulDestructible
pool.release (ptr); pool.release (ptr);
} }
private: private:
static Pool pool; static Pool pool;
}; };
@ -1620,9 +1621,9 @@ class Session : public PBD::StatefulDestructible
nframes_t click_emphasis_length; nframes_t click_emphasis_length;
mutable Glib::RWLock click_lock; mutable Glib::RWLock click_lock;
static const Sample default_click[]; static const Sample default_click[];
static const nframes_t default_click_length; static const nframes_t default_click_length;
static const Sample default_click_emphasis[]; static const Sample default_click_emphasis[];
static const nframes_t default_click_emphasis_length; static const nframes_t default_click_emphasis_length;
Click *get_click(); Click *get_click();

View file

@ -22,13 +22,16 @@
#include <ardour/midi_model.h> #include <ardour/midi_model.h>
#include <ardour/midi_events.h> #include <ardour/midi_events.h>
#include <ardour/types.h> #include <ardour/types.h>
#include <ardour/session.h>
using namespace std; using namespace std;
using namespace ARDOUR; using namespace ARDOUR;
MidiModel::MidiModel(size_t size) MidiModel::MidiModel(Session& s, size_t size)
: _notes(size) : _session(s)
, _notes(size)
, _command(NULL)
{ {
} }
@ -140,3 +143,98 @@ MidiModel::append_note_off(double time, uint8_t note_num)
} }
} }
void
MidiModel::add_note(const Note& note)
{
Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note, NoteTimeComparator());
_notes.insert(i, note);
if (_command)
_command->add_note(note);
}
void
MidiModel::remove_note(const Note& note)
{
Notes::iterator n = find(_notes.begin(), _notes.end(), note);
if (n != _notes.end())
_notes.erase(n);
if (_command)
_command->remove_note(note);
}
void
MidiModel::begin_command()
{
assert(!_command);
_session.begin_reversible_command("midi edit");
_command = new MidiEditCommand(*this);
}
void
MidiModel::finish_command()
{
_session.commit_reversible_command(_command);
_command = NULL;
}
// MidiEditCommand
void
MidiModel::MidiEditCommand::add_note(const Note& note)
{
//cerr << "MEC: apply" << endl;
_removed_notes.remove(note);
_added_notes.push_back(note);
}
void
MidiModel::MidiEditCommand::remove_note(const Note& note)
{
//cerr << "MEC: remove" << endl;
_added_notes.remove(note);
_removed_notes.push_back(note);
}
void
MidiModel::MidiEditCommand::operator()()
{
//cerr << "MEC: apply" << endl;
assert(!_model.current_command());
for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
_model.add_note(*i);
for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
_model.remove_note(*i);
_model.ContentsChanged(); /* EMIT SIGNAL */
}
void
MidiModel::MidiEditCommand::undo()
{
//cerr << "MEC: undo" << endl;
assert(!_model.current_command());
for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
_model.remove_note(*i);
for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
_model.add_note(*i);
_model.ContentsChanged(); /* EMIT SIGNAL */
}

View file

@ -44,7 +44,7 @@ sigc::signal<void,MidiSource *> MidiSource::MidiSourceCreated;
MidiSource::MidiSource (Session& s, string name) MidiSource::MidiSource (Session& s, string name)
: Source (s, name, DataType::MIDI) : Source (s, name, DataType::MIDI)
, _model(new MidiModel()) , _model(new MidiModel(s))
, _model_loaded (false) , _model_loaded (false)
{ {
_read_data_count = 0; _read_data_count = 0;
@ -53,7 +53,7 @@ MidiSource::MidiSource (Session& s, string name)
MidiSource::MidiSource (Session& s, const XMLNode& node) MidiSource::MidiSource (Session& s, const XMLNode& node)
: Source (s, node) : Source (s, node)
, _model(new MidiModel()) , _model(new MidiModel(s))
, _model_loaded (false) , _model_loaded (false)
{ {
_read_data_count = 0; _read_data_count = 0;

View file

@ -787,7 +787,7 @@ SMFSource::load_model(bool lock, bool force_reload)
} }
if (! _model) if (! _model)
_model = new MidiModel(); _model = new MidiModel(_session);
_model->start_write(); _model->start_write();