fix thinko when dealing with non-MIDI tracks
[ardour.git] / libs / ardour / midi_ring_buffer.cc
1 /*
2  * Copyright (C) 2008-2016 David Robillard <d@drobilla.net>
3  * Copyright (C) 2009-2017 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2009 Hans Baier <hansfbaier@googlemail.com>
5  * Copyright (C) 2010-2011 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2014-2015 Robin Gareus <robin@gareus.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include "pbd/compose.h"
24 #include "pbd/enumwriter.h"
25 #include "pbd/error.h"
26
27 #include "ardour/debug.h"
28 #include "ardour/midi_ring_buffer.h"
29 #include "ardour/midi_buffer.h"
30 #include "ardour/event_type_map.h"
31
32 using namespace std;
33 using namespace PBD;
34
35 namespace ARDOUR {
36
37 /** Read a block of MIDI events from this buffer into a MidiBuffer.
38  *
39  * Timestamps of events returned are relative to start (i.e. event with stamp 0
40  * occurred at start), with offset added.
41  */
42 template<typename T>
43 size_t
44 MidiRingBuffer<T>::read (MidiBuffer& dst, samplepos_t start, samplepos_t end, samplecnt_t offset, bool stop_on_overflow_in_dst)
45 {
46         if (this->read_space() == 0) {
47                 return 0;
48         }
49
50         T                 ev_time;
51         uint32_t          ev_size;
52         size_t            count = 0;
53         const size_t      prefix_size = sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t);
54
55         while (this->read_space() >= prefix_size) {
56
57                 uint8_t peekbuf[prefix_size];
58
59                 /* this cannot fail, because we've already verified that there
60                    is prefix_space to read
61                 */
62                 this->peek (peekbuf, prefix_size);
63
64                 ev_time = *(reinterpret_cast<T*>((uintptr_t)peekbuf));
65                 ev_size = *(reinterpret_cast<uint32_t*>((uintptr_t)(peekbuf + sizeof(T) + sizeof (Evoral::EventType))));
66
67                 if (this->read_space() < ev_size) {
68                         break;;
69                 }
70
71                 if (ev_time >= end) {
72                         DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("MRB event @ %1 past end @ %2\n", ev_time, end));
73                         break;
74                 } else if (ev_time < start) {
75                         DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("MRB event @ %1 before start @ %2\n", ev_time, start));
76                         break;
77                 } else {
78                         DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("MRB event @ %1 in range %2 .. %3\n", ev_time, start, end));
79                 }
80
81                 ev_time -= start;
82                 ev_time += offset;
83
84                 /* we're good to go ahead and read the data now but since we
85                  * have the prefix data already, just skip over that
86                  */
87                 this->increment_read_ptr (prefix_size);
88
89                 uint8_t status;
90                 bool r = this->peek (&status, sizeof(uint8_t));
91                 assert (r); // If this failed, buffer is corrupt, all hope is lost
92
93                 /* lets see if we are going to be able to write this event into dst.
94                  */
95                 uint8_t* write_loc = dst.reserve (ev_time, ev_size);
96                 if (write_loc == 0) {
97                         if (stop_on_overflow_in_dst) {
98                                 DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("MidiRingBuffer: overflow in destination MIDI buffer, stopped after %1 events\n", count));
99                                 break;
100                         }
101                         error << "MRB: Unable to reserve space in buffer, event skipped" << endmsg;
102                         this->increment_read_ptr (ev_size); // Advance read pointer to next event
103                         continue;
104                 }
105
106                 // write MIDI buffer contents
107
108                 bool success = read_contents (ev_size, write_loc);
109 #ifndef NDEBUG
110                 if (DEBUG_ENABLED (DEBUG::MidiRingBuffer)) {
111                         DEBUG_STR_DECL(a);
112                         DEBUG_STR_APPEND(a, string_compose ("wrote MidiEvent to Buffer (time=%1, start=%2 offset=%3) ", ev_time, start, offset));
113                         for (size_t i=0; i < ev_size; ++i) {
114                                 DEBUG_STR_APPEND(a,hex);
115                                 DEBUG_STR_APPEND(a,"0x");
116                                 DEBUG_STR_APPEND(a,(int)write_loc[i]);
117                                 DEBUG_STR_APPEND(a,' ');
118                         }
119                         DEBUG_STR_APPEND(a,'\n');
120                         DEBUG_TRACE (DEBUG::MidiRingBuffer, DEBUG_STR(a).str());
121                 }
122 #endif
123                 if (success) {
124                         _tracker.track(write_loc);
125                         ++count;
126                 } else {
127                         cerr << "WARNING: error reading event contents from MIDI ring" << endl;
128                 }
129         }
130
131         return count;
132 }
133
134 template<typename T>
135 size_t
136 MidiRingBuffer<T>::skip_to(samplepos_t start)
137 {
138         if (this->read_space() == 0) {
139                 return 0;
140         }
141
142         T                 ev_time;
143         uint32_t          ev_size;
144         size_t            count = 0;
145         const size_t      prefix_size = sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t);
146
147         while (this->read_space() >= prefix_size) {
148
149                 uint8_t peekbuf[prefix_size];
150                 this->peek (peekbuf, prefix_size);
151
152                 ev_time = *(reinterpret_cast<T*>((uintptr_t)peekbuf));
153                 ev_size = *(reinterpret_cast<uint32_t*>((uintptr_t)(peekbuf + sizeof(T) + sizeof (Evoral::EventType))));
154
155                 if (ev_time >= start) {
156                         return count;
157                 }
158
159                 if (this->read_space() < ev_size) {
160                         continue;
161                 }
162
163                 this->increment_read_ptr (prefix_size);
164
165                 uint8_t status;
166                 bool r = this->peek (&status, sizeof(uint8_t));
167                 assert (r); // If this failed, buffer is corrupt, all hope is lost
168
169                 ++count;
170
171                 /* TODO investigate and think:
172                  *
173                  * Does it makes sense to keep track of notes
174                  * that are skipped (because they're either too late
175                  * (underrun) or never used (read-ahead, loop) ?
176                  *
177                  * skip_to() is called on the rinbuffer between
178                  * disk and process. it seems wrong to track them
179                  * (a potential synth never sees skipped notes, either)
180                  * but there may be more to this.
181                  */
182
183                 if (ev_size >= 8) {
184                         this->increment_read_ptr (ev_size);
185                 } else {
186                         // we only track note on/off, 8 bytes are plenty.
187                         uint8_t write_loc[8];
188                         bool success = read_contents (ev_size, write_loc);
189                         if (success) {
190                                 _tracker.track(write_loc);
191                         }
192                 }
193         }
194         return count;
195 }
196
197
198
199 template<typename T>
200 void
201 MidiRingBuffer<T>::flush (samplepos_t /*start*/, samplepos_t end)
202 {
203         const size_t prefix_size = sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t);
204
205         while (this->read_space() >= prefix_size) {
206                 uint8_t  peekbuf[prefix_size];
207                 bool     success;
208                 uint32_t ev_size;
209                 T        ev_time;
210
211                 success = this->peek (peekbuf, prefix_size);
212                 /* this cannot fail, because we've already verified that there
213                    is prefix_space to read
214                 */
215                 assert (success);
216
217                 ev_time = *(reinterpret_cast<T*>((uintptr_t)peekbuf));
218
219                 if (ev_time >= end) {
220                         break;
221                 }
222
223                 ev_size = *(reinterpret_cast<uint32_t*>((uintptr_t)(peekbuf + sizeof(T) + sizeof (Evoral::EventType))));
224                 this->increment_read_ptr (prefix_size);
225                 this->increment_read_ptr (ev_size);
226         }
227 }
228
229 template<typename T>
230 void
231 MidiRingBuffer<T>::dump(ostream& str)
232 {
233         size_t rspace;
234
235         if ((rspace = this->read_space()) == 0) {
236                 str << this << " MRB::dump: empty\n";
237                 return;
238         }
239
240         T                 ev_time;
241         Evoral::EventType ev_type;
242         uint32_t          ev_size;
243
244         RingBufferNPT<uint8_t>::rw_vector vec;
245         RingBufferNPT<uint8_t>::get_read_vector (&vec);
246
247         if (vec.len[0] == 0) {
248                 return;
249         }
250
251         str << this << ": Dump size = " << vec.len[0] + vec.len[1]
252             << " r@ " << RingBufferNPT<uint8_t>::get_read_ptr()
253             << " w@" << RingBufferNPT<uint8_t>::get_write_ptr() << endl;
254
255
256         uint8_t *buf = new uint8_t[vec.len[0] + vec.len[1]];
257         memcpy (buf, vec.buf[0], vec.len[0]);
258
259         if (vec.len[1]) {
260                 memcpy (buf+vec.len[1], vec.buf[1], vec.len[1]);
261         }
262
263         uint8_t* data = buf;
264         const uint8_t* end = buf + vec.len[0] + vec.len[1];
265
266         while (data < end) {
267
268                 memcpy (&ev_time, data, sizeof (T));
269                 data += sizeof (T);
270                 str << "\ttime " << ev_time;
271
272                 if (data >= end) {
273                         str << "(incomplete)\n ";
274                         break;
275                 }
276
277                 memcpy (&ev_type, data, sizeof (ev_type));
278                 data += sizeof (ev_type);
279                 str << " type " << ev_type;
280
281                 if (data >= end) {
282                         str << "(incomplete)\n";
283                         break;
284                 }
285
286                 memcpy (&ev_size, data, sizeof (ev_size));
287                 data += sizeof (ev_size);
288                 str << " size " << ev_size;
289
290                 if (data >= end) {
291                         str << "(incomplete)\n";
292                         break;
293                 }
294
295                 for (uint32_t i = 0; i != ev_size && data < end; ++i) {
296                         str << ' ' << hex << (int) data[i] << dec;
297                 }
298
299                 data += ev_size;
300
301                 str << endl;
302         }
303
304         delete [] buf;
305 }
306
307 template<typename T>
308 void
309 MidiRingBuffer<T>::reset_tracker ()
310 {
311         _tracker.reset ();
312 }
313
314 template<typename T>
315 void
316 MidiRingBuffer<T>::resolve_tracker (MidiBuffer& dst, samplepos_t t)
317 {
318         _tracker.resolve_notes (dst, t);
319 }
320
321 template<typename T>
322 void
323 MidiRingBuffer<T>::resolve_tracker (Evoral::EventSink<samplepos_t>& dst, samplepos_t t)
324 {
325         _tracker.resolve_notes(dst, t);
326 }
327
328 template class MidiRingBuffer<samplepos_t>;
329
330 }  // namespace ARDOUR