2 * Copyright (C) 2017 Paul Davis <paul@linuxaudiosystems.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "evoral/EventList.hpp"
21 #include "ardour/beats_samples_converter.h"
22 #include "ardour/midi_state_tracker.h"
23 #include "ardour/note_fixer.h"
24 #include "ardour/tempo.h"
28 NoteFixer::~NoteFixer()
36 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
42 NoteFixer::prepare(TempoMap& tempo_map,
43 const MidiModel::NoteDiffCommand* cmd,
44 const samplepos_t origin,
45 const samplepos_t pos,
46 std::set< boost::weak_ptr<Note> >& active_notes)
48 typedef MidiModel::NoteDiffCommand Command;
50 BeatsSamplesConverter converter(tempo_map, origin);
52 for (Command::NoteList::const_iterator i = cmd->removed_notes().begin();
53 i != cmd->removed_notes().end(); ++i) {
54 if (note_is_active(converter, *i, pos)) {
55 /* Deleted note spans the end of the latest read, so we will never
56 read its off event. Emit a note off to prevent a stuck note. */
57 _events.push_back(copy_event(pos, (*i)->off_event()));
58 active_notes.erase(*i);
62 for (Command::NoteList::const_iterator i = cmd->added_notes().begin();
63 i != cmd->added_notes().end(); ++i) {
64 if (note_is_active(converter, *i, pos)) {
65 /* Added note spans the end of the latest read, so we missed its on
66 event. Emit note on immediately to make the state consistent. */
67 _events.push_back(copy_event(pos, (*i)->on_event()));
68 active_notes.insert(*i);
72 for (Command::ChangeList::const_iterator i = cmd->changes().begin();
73 i != cmd->changes().end(); ++i) {
74 if (!note_is_active(converter, i->note, pos)) {
75 /* Note is not currently active, no compensation needed. */
79 /* Changed note spans the end of the latest read. */
80 if (i->property == Command::NoteNumber) {
81 /* Note number has changed, end the old note. */
82 _events.push_back(copy_event(pos, i->note->off_event()));
84 /* Start a new note on the new note number. The same note object
85 is active, so we leave active_notes alone. */
86 Event* on = copy_event(pos, i->note->on_event());
87 on->buffer()[1] = (uint8_t)i->new_value.get_int();
88 _events.push_back(on);
89 } else if (i->property == Command::StartTime &&
90 converter.to(i->new_value.get_beats()) >= pos) {
91 /* Start time has moved from before to after the end of the
92 latest read, end the old note. */
93 _events.push_back(copy_event(pos, i->note->off_event()));
94 active_notes.erase(i->note);
95 } else if (i->property == Command::Length &&
96 converter.to(i->note->time() + i->new_value.get_beats()) < pos) {
97 /* Length has shortened to before the end of the latest read,
99 _events.push_back(copy_event(pos, i->note->off_event()));
100 active_notes.erase(i->note);
101 } else if (i->property == Command::Channel) {
102 /* Channel has changed, end the old note. */
103 _events.push_back(copy_event(pos, i->note->off_event()));
105 /* Start a new note on the new channel. See number change above. */
106 Event* on = copy_event(pos, i->note->on_event());
107 on->buffer()[0] &= 0xF0;
108 on->buffer()[0] |= (uint8_t)i->new_value.get_int();
109 _events.push_back(on);
115 NoteFixer::emit(Evoral::EventSink<samplepos_t>& dst,
117 MidiStateTracker& tracker)
119 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
120 dst.write(pos, (*i)->event_type(), (*i)->size(), (*i)->buffer());
128 NoteFixer::copy_event(samplepos_t time, const Evoral::Event<Temporal::Beats>& ev)
130 return new Event(ev.event_type(), time, ev.size(), ev.buffer());
134 NoteFixer::note_is_active(const BeatsSamplesConverter& converter,
135 boost::shared_ptr<Note> note,
138 const samplepos_t start_time = converter.to(note->time());
139 const samplepos_t end_time = converter.to(note->end_time());
141 return (start_time < pos && end_time >= pos);
144 } // namespace ARDOUR