2 Copyright (C) 2015 Paul Davis
3 Author: David Robillard
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.
20 #include "evoral/EventList.hpp"
22 #include "ardour/beats_samples_converter.h"
23 #include "ardour/midi_state_tracker.h"
24 #include "ardour/note_fixer.h"
25 #include "ardour/tempo.h"
29 NoteFixer::~NoteFixer()
37 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
43 NoteFixer::prepare(TempoMap& tempo_map,
44 const MidiModel::NoteDiffCommand* cmd,
45 const samplepos_t origin,
46 const samplepos_t pos,
47 std::set< boost::weak_ptr<Note> >& active_notes)
49 typedef MidiModel::NoteDiffCommand Command;
51 BeatsSamplesConverter converter(tempo_map, origin);
53 for (Command::NoteList::const_iterator i = cmd->removed_notes().begin();
54 i != cmd->removed_notes().end(); ++i) {
55 if (note_is_active(converter, *i, pos)) {
56 /* Deleted note spans the end of the latest read, so we will never
57 read its off event. Emit a note off to prevent a stuck note. */
58 _events.push_back(copy_event(pos, (*i)->off_event()));
59 active_notes.erase(*i);
63 for (Command::NoteList::const_iterator i = cmd->added_notes().begin();
64 i != cmd->added_notes().end(); ++i) {
65 if (note_is_active(converter, *i, pos)) {
66 /* Added note spans the end of the latest read, so we missed its on
67 event. Emit note on immediately to make the state consistent. */
68 _events.push_back(copy_event(pos, (*i)->on_event()));
69 active_notes.insert(*i);
73 for (Command::ChangeList::const_iterator i = cmd->changes().begin();
74 i != cmd->changes().end(); ++i) {
75 if (!note_is_active(converter, i->note, pos)) {
76 /* Note is not currently active, no compensation needed. */
80 /* Changed note spans the end of the latest read. */
81 if (i->property == Command::NoteNumber) {
82 /* Note number has changed, end the old note. */
83 _events.push_back(copy_event(pos, i->note->off_event()));
85 /* Start a new note on the new note number. The same note object
86 is active, so we leave active_notes alone. */
87 Event* on = copy_event(pos, i->note->on_event());
88 on->buffer()[1] = (uint8_t)i->new_value.get_int();
89 _events.push_back(on);
90 } else if (i->property == Command::StartTime &&
91 converter.to(i->new_value.get_beats()) >= pos) {
92 /* Start time has moved from before to after the end of the
93 latest read, end the old note. */
94 _events.push_back(copy_event(pos, i->note->off_event()));
95 active_notes.erase(i->note);
96 } else if (i->property == Command::Length &&
97 converter.to(i->note->time() + i->new_value.get_beats()) < pos) {
98 /* Length has shortened to before the end of the latest read,
100 _events.push_back(copy_event(pos, i->note->off_event()));
101 active_notes.erase(i->note);
102 } else if (i->property == Command::Channel) {
103 /* Channel has changed, end the old note. */
104 _events.push_back(copy_event(pos, i->note->off_event()));
106 /* Start a new note on the new channel. See number change above. */
107 Event* on = copy_event(pos, i->note->on_event());
108 on->buffer()[0] &= 0xF0;
109 on->buffer()[0] |= (uint8_t)i->new_value.get_int();
110 _events.push_back(on);
116 NoteFixer::emit(Evoral::EventSink<samplepos_t>& dst,
118 MidiStateTracker& tracker)
120 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
121 dst.write(pos, (*i)->event_type(), (*i)->size(), (*i)->buffer());
129 NoteFixer::copy_event(samplepos_t time, const Evoral::Event<Temporal::Beats>& ev)
131 return new Event(ev.event_type(), time, ev.size(), ev.buffer());
135 NoteFixer::note_is_active(const BeatsSamplesConverter& converter,
136 boost::shared_ptr<Note> note,
139 const samplepos_t start_time = converter.to(note->time());
140 const samplepos_t end_time = converter.to(note->end_time());
142 return (start_time < pos && end_time >= pos);
145 } // namespace ARDOUR