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