a grab bag of changes correcting and improving the way MIDI note on/off tracking...
[ardour.git] / libs / ardour / midi_ring_buffer.cc
1 /*
2     Copyright (C) 2006-2008 Paul Davis
3
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.
8
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.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include "pbd/compose.h"
20 #include "pbd/error.h"
21
22 #include "ardour/debug.h"
23 #include "ardour/midi_ring_buffer.h"
24 #include "ardour/midi_buffer.h"
25 #include "ardour/event_type_map.h"
26
27 using namespace std;
28 using namespace ARDOUR;
29 using namespace PBD;
30
31 /** Read a block of MIDI events from this buffer into a MidiBuffer.
32  *
33  * Timestamps of events returned are relative to start (i.e. event with stamp 0
34  * occurred at start), with offset added.
35  */
36 template<typename T>
37 size_t
38 MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset, bool stop_on_overflow_in_dst)
39 {
40         if (this->read_space() == 0) {
41                 return 0;
42         }
43
44         T                 ev_time;
45         Evoral::EventType ev_type;
46         uint32_t          ev_size;
47
48         /* If we see the end of a loop during this read, we must write the events after it
49            to the MidiBuffer with adjusted times.  The situation is as follows:
50
51            session frames----------------------------->
52
53                      |                            |                    |
54                 start_of_loop                   start              end_of_loop
55
56            The MidiDiskstream::read method which will have happened before this checks for
57            loops ending, and helpfully inserts a magic LoopEvent into the ringbuffer.  After this,
58            the MidiDiskstream continues to write events with their proper session frame times,
59            so after the LoopEvent event times will go backwards (ie non-monotonically).
60
61            Once we hit end_of_loop, we need to fake it to make it look as though the loop has been
62            immediately repeated.  Say that an event E after the end_of_loop in the ringbuffer
63            has time E_t, which is a time in session frames.  Its offset from the start
64            of the loop will be E_t - start_of_loop.  Its `faked' time will therefore be
65            end_of_loop + E_t - start_of_loop.  And so its port-buffer-relative time (for
66            writing to the MidiBuffer) will be end_of_loop + E_t - start_of_loop - start.
67
68            The subtraction of start is already taken care of, so if we see a LoopEvent, we'll
69            set up loop_offset to equal end_of_loop - start_of_loop, so that given an event
70            time E_t in the ringbuffer we can get the port-buffer-relative time as
71            E_t + offset - start.
72         */
73
74         frameoffset_t loop_offset = 0;
75
76         size_t count = 0;
77
78         const size_t prefix_size = sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t);
79
80         while (this->read_space() >= prefix_size) {
81
82                 uint8_t peekbuf[prefix_size];
83                 bool success;
84
85                 success = this->peek (peekbuf, prefix_size);
86                 /* this cannot fail, because we've already verified that there
87                    is prefix_space to read
88                 */
89                 assert (success);
90
91                 ev_time = *((T*) peekbuf);
92                 ev_type = *((Evoral::EventType*)(peekbuf + sizeof (T)));
93                 ev_size = *((uint32_t*)(peekbuf + sizeof(T) + sizeof (Evoral::EventType)));
94
95                 if (ev_time + loop_offset >= end) {
96                         DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 past end @ %2\n", ev_time, end));
97                         break;
98                 } else if (ev_time + loop_offset < start) {
99                         DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 before start @ %2\n", ev_time, start));
100                         break;
101                 } else {
102                         DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 in range %2 .. %3\n", ev_time, start, end));
103                 }
104
105                 assert(ev_time >= start);
106
107                 ev_time -= start;
108                 ev_time += offset;
109
110                 // This event marks a loop end (i.e. the next event's timestamp
111                 // will be non-monotonic). Don't write it into the buffer - the
112                 // significance of this event ends here.
113                 
114                 if (ev_type == LoopEventType) {
115                         assert (ev_size == sizeof (framepos_t));
116                         framepos_t loop_start;
117                         read_contents (ev_size, (uint8_t *) &loop_start);
118                         loop_offset = ev_time - loop_start;
119                         _tracker.resolve_notes (dst, ev_time);
120                         continue;
121                 }
122
123                 /* we're good to go ahead and read the data now but since we
124                  * have the prefix data already, just skip over that
125                  */
126                 this->increment_read_ptr (prefix_size);
127                 ev_time += loop_offset;
128
129                 uint8_t status;
130                 success = this->peek (&status, sizeof(uint8_t));
131                 assert(success); // If this failed, buffer is corrupt, all hope is lost
132
133                 // Ignore event if it doesn't match channel filter
134                 if (is_channel_event(status) && get_channel_mode() == FilterChannels) {
135                         const uint8_t channel = status & 0x0F;
136                         if (!(get_channel_mask() & (1L << channel))) {
137                                 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB skipping event (%3 bytes) due to channel mask (mask = %1 chn = %2)\n",
138                                                                                       get_channel_mask(), (int) channel, ev_size));
139                                 this->increment_read_ptr (ev_size); // Advance read pointer to next event
140                                 continue;
141                         }
142                 }
143
144                 /* lets see if we are going to be able to write this event into dst.
145                  */
146                 uint8_t* write_loc = dst.reserve (ev_time, ev_size);
147                 if (write_loc == 0) {
148                         if (stop_on_overflow_in_dst) {
149                                 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MidiRingBuffer: overflow in destination MIDI buffer, stopped after %1 events\n", count));
150                                 break;
151                         }
152                         error << "MRB: Unable to reserve space in buffer, event skipped" << endmsg;
153                         this->increment_read_ptr (ev_size); // Advance read pointer to next event
154                         continue;
155                 }
156
157                 // write MIDI buffer contents
158                 success = read_contents (ev_size, write_loc);
159
160 #ifndef NDEBUG
161                 if (DEBUG::MidiDiskstreamIO && PBD::debug_bits) {
162                         DEBUG_STR_DECL(a);
163                         DEBUG_STR_APPEND(a, string_compose ("wrote MidiEvent to Buffer (time=%1, start=%2 offset=%3)", ev_time, start, offset));
164                         for (size_t i=0; i < ev_size; ++i) {
165                                 DEBUG_STR_APPEND(a,hex);
166                                 DEBUG_STR_APPEND(a,"0x");
167                                 DEBUG_STR_APPEND(a,(int)write_loc[i]);
168                                 DEBUG_STR_APPEND(a,' ');
169                         }
170                         DEBUG_STR_APPEND(a,'\n');
171                         DEBUG_TRACE (DEBUG::MidiDiskstreamIO, DEBUG_STR(a).str());
172                 }
173 #endif
174
175                 if (success) {
176
177                         if (is_note_on(write_loc[0]) ) {
178                                 _tracker.add (write_loc[1], write_loc[0] & 0xf);
179                         } else if (is_note_off(write_loc[0])) {
180                                 _tracker.remove (write_loc[1], write_loc[0] & 0xf);
181                         }
182                         
183                         if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
184                                 write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F);
185                         }
186                         ++count;
187                 } else {
188                         cerr << "WARNING: error reading event contents from MIDI ring" << endl;
189                 }
190         }
191
192         return count;
193 }
194
195 template<typename T>
196 void
197 MidiRingBuffer<T>::dump(ostream& str)
198 {
199         size_t rspace;
200
201         if ((rspace = this->read_space()) == 0) {
202                 str << "MRB::dump: empty\n";
203                 return;
204         }
205
206         T                 ev_time;
207         Evoral::EventType ev_type;
208         uint32_t          ev_size;
209
210         RingBufferNPT<uint8_t>::rw_vector vec;
211         RingBufferNPT<uint8_t>::get_read_vector (&vec);
212
213         if (vec.len[0] == 0) {
214                 return;
215         }
216
217         str << this << ": Dump size = " << vec.len[0] + vec.len[1]
218             << " r@ " << RingBufferNPT<uint8_t>::get_read_ptr()
219             << " w@" << RingBufferNPT<uint8_t>::get_write_ptr() << endl;
220
221
222         uint8_t *buf = new uint8_t[vec.len[0] + vec.len[1]];
223         memcpy (buf, vec.buf[0], vec.len[0]);
224
225         if (vec.len[1]) {
226                 memcpy (buf+vec.len[1], vec.buf[1], vec.len[1]);
227         }
228
229         uint8_t* data = buf;
230         const uint8_t* end = buf + vec.len[0] + vec.len[1];
231
232         while (data < end) {
233
234                 memcpy (&ev_time, data, sizeof (T));
235                 data += sizeof (T);
236                 str << "\ttime " << ev_time;
237
238                 if (data >= end) {
239                         str << "(incomplete)\n ";
240                         break;
241                 }
242
243                 memcpy (&ev_type, data, sizeof (ev_type));
244                 data += sizeof (ev_type);
245                 str << " type " << ev_type;
246
247                 if (data >= end) {
248                         str << "(incomplete)\n";
249                         break;
250                 }
251
252                 memcpy (&ev_size, data, sizeof (ev_size));
253                 data += sizeof (ev_size);
254                 str << " size " << ev_size;
255
256                 if (data >= end) {
257                         str << "(incomplete)\n";
258                         break;
259                 }
260
261                 for (uint32_t i = 0; i != ev_size && data < end; ++i) {
262                         str << ' ' << hex << (int) data[i] << dec;
263                 }
264
265                 data += ev_size;
266
267                 str << endl;
268         }
269
270         delete [] buf;
271 }
272
273 template<typename T>
274 void
275 MidiRingBuffer<T>::reset_tracker ()
276 {
277         _tracker.reset ();
278 }
279
280 template class MidiRingBuffer<framepos_t>;
281