2 Copyright (C) 2007 Paul Davis
3 Written by Dave Robillard, 2007
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <ardour/midi_model.h>
23 #include <ardour/midi_events.h>
24 #include <ardour/types.h>
25 #include <ardour/session.h>
28 using namespace ARDOUR;
31 MidiModel::MidiModel(Session& s, size_t size)
39 /** Begin a write of events to the model.
41 * As note on and off events are written, complete notes with duration are
45 MidiModel::start_write()
52 /** Finish a write of events to the model.
54 * If \a delete_stuck is true, note on events that were never resolved with
55 * a corresonding note off will be deleted. Otherwise they will remain as
56 * notes with duration 0.
59 MidiModel::end_write(bool delete_stuck)
64 cerr << "FIXME: Stuck notes lost" << endl;
66 /* Merge _write_events into _events */
68 size_t write_index = 0;
69 while ( ! _write_events.empty()) {
76 /** Append contents of \a buf to model. NOT realtime safe.
78 * Timestamps of events in \a buf are expected to be relative to
79 * the start of this model (t=0) and MUST be monotonically increasing
80 * and MUST be >= the latest event currently in the model.
82 * Events in buf are deep copied.
85 MidiModel::append(const MidiBuffer& buf)
87 for (size_t i=0; i < buf.size(); ++i) {
88 const MidiEvent& ev = buf[i];
90 assert(_write_notes.empty() || ev.time >= _write_notes.back().start);
92 if (ev.type() == MIDI_CMD_NOTE_ON)
93 append_note_on(ev.time, ev.note(), ev.velocity());
94 else if (ev.type() == MIDI_CMD_NOTE_OFF)
95 append_note_off(ev.time, ev.note());
100 /** Append \a in_event to model. NOT realtime safe.
102 * Timestamps of events in \a buf are expected to be relative to
103 * the start of this model (t=0) and MUST be monotonically increasing
104 * and MUST be >= the latest event currently in the model.
107 MidiModel::append(double time, size_t size, Byte* buf)
109 assert(_write_notes.empty() || time >= _write_notes.back().start);
111 if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_ON)
112 append_note_on(time, buf[1], buf[2]);
113 else if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_OFF)
114 append_note_off(time, buf[1]);
119 MidiModel::append_note_on(double time, uint8_t note, uint8_t velocity)
121 _write_notes.push_back(Note(time, 0, note, velocity));
126 MidiModel::append_note_off(double time, uint8_t note_num)
128 /* _write_notes (active notes) is presumably small enough for linear
129 * search to be a good idea. maybe not with instruments (percussion)
130 * that don't send note off at all though.... FIXME? */
132 /* FIXME: note off velocity for that one guy out there who actually has
133 * keys that send it */
135 for (size_t i=0; i < _write_notes.size(); ++i) {
136 Note& note = _write_notes[i];
137 if (note.note == note_num) {
138 assert(time > note.start);
139 note.duration = time - note.start;
140 cerr << "MidiModel resolved note, duration: " << note.duration << endl;
148 MidiModel::add_note(const Note& note)
150 Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note, NoteTimeComparator());
151 _notes.insert(i, note);
153 _command->add_note(note);
158 MidiModel::remove_note(const Note& note)
160 Notes::iterator n = find(_notes.begin(), _notes.end(), note);
161 if (n != _notes.end())
165 _command->remove_note(note);
171 MidiModel::begin_command()
174 _session.begin_reversible_command("midi edit");
175 _command = new MidiEditCommand(*this);
180 MidiModel::finish_command()
182 _session.commit_reversible_command(_command);
191 MidiModel::MidiEditCommand::add_note(const Note& note)
193 //cerr << "MEC: apply" << endl;
195 _removed_notes.remove(note);
196 _added_notes.push_back(note);
201 MidiModel::MidiEditCommand::remove_note(const Note& note)
203 //cerr << "MEC: remove" << endl;
205 _added_notes.remove(note);
206 _removed_notes.push_back(note);
211 MidiModel::MidiEditCommand::operator()()
213 //cerr << "MEC: apply" << endl;
214 assert(!_model.current_command());
216 for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
219 for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
220 _model.remove_note(*i);
222 _model.ContentsChanged(); /* EMIT SIGNAL */
227 MidiModel::MidiEditCommand::undo()
229 //cerr << "MEC: undo" << endl;
230 assert(!_model.current_command());
232 for (std::list<Note>::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
233 _model.remove_note(*i);
235 for (std::list<Note>::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
238 _model.ContentsChanged(); /* EMIT SIGNAL */