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