ardour
note_fixer.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2015 Paul Davis
3  Author: David Robillard
4 
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.
9 
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.
14 
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.
18 */
19 
20 #include "evoral/EventList.hpp"
21 
24 #include "ardour/note_fixer.h"
25 #include "ardour/tempo.h"
26 
27 namespace ARDOUR {
28 
30 {
31  clear();
32 }
33 
34 void
36 {
37  for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
38  delete *i;
39  }
40 }
41 
42 void
44  const MidiModel::NoteDiffCommand* cmd,
45  const framepos_t origin,
46  const framepos_t pos,
47  std::set< boost::weak_ptr<Note> >& active_notes)
48 {
50 
51  BeatsFramesConverter converter(tempo_map, origin);
52 
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);
60  }
61  }
62 
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);
70  }
71  }
72 
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. */
77  continue;
78  }
79 
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()));
84 
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,
99  end the note. */
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()));
105 
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);
111  }
112  }
113 }
114 
115 void
117  framepos_t pos,
118  MidiStateTracker& tracker)
119 {
120  for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
121  dst.write(pos, (*i)->event_type(), (*i)->size(), (*i)->buffer());
122  tracker.track(**i);
123  delete *i;
124  }
125  _events.clear();
126 }
127 
130 {
131  return new Event(ev.event_type(), time, ev.size(), ev.buffer());
132 }
133 
134 bool
137  framepos_t pos)
138 {
139  const framepos_t start_time = converter.to(note->time());
140  const framepos_t end_time = converter.to(note->end_time());
141 
142  return (start_time < pos && end_time >= pos);
143 }
144 
145 } // namespace ARDOUR
Evoral::Event< framepos_t > Event
Definition: note_fixer.h:84
EventType event_type() const
Definition: Event.hpp:131
virtual uint32_t write(Time time, EventType type, uint32_t size, const uint8_t *buf)=0
const NoteList & removed_notes() const
Definition: midi_model.h:142
void prepare(TempoMap &tempo_map, const MidiModel::NoteDiffCommand *cmd, framepos_t origin, framepos_t pos, std::set< boost::weak_ptr< Note > > &active_notes)
Definition: note_fixer.cc:43
const NoteList & added_notes() const
Definition: midi_model.h:141
Time time() const
Definition: Note.hpp:60
void track(const MidiBuffer::const_iterator &from, const MidiBuffer::const_iterator &to)
Event * copy_event(framepos_t time, const Evoral::Event< Evoral::Beats > &ev)
Definition: note_fixer.cc:129
#define origin
bool note_is_active(const BeatsFramesConverter &converter, boost::shared_ptr< Note > note, framepos_t pos)
Definition: note_fixer.cc:135
void emit(Evoral::EventSink< framepos_t > &dst, framepos_t pos, MidiStateTracker &tracker)
Definition: note_fixer.cc:116
const ChangeList & changes() const
Definition: midi_model.h:140
uint32_t size() const
Definition: Event.hpp:134
Definition: amp.h:29
int64_t framepos_t
Definition: types.h:66
Time end_time() const
Definition: Note.hpp:61
const uint8_t * buffer() const
Definition: Event.hpp:135
framepos_t to(Evoral::Beats beats) const