new record-step-edit icon for track/bus list
[ardour2.git] / libs / ardour / midi_ring_buffer.cc
blob34a1bafc76fb8dc9d595a65a67cc535fca9d000a
1 /*
2 Copyright (C) 2006-2008 Paul Davis
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.
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.
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.
19 #include "pbd/compose.h"
20 #include "pbd/error.h"
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"
27 using namespace std;
28 using namespace ARDOUR;
29 using namespace PBD;
31 /** Read a block of MIDI events from this buffer into a MidiBuffer.
33 * Timestamps of events returned are relative to start (i.e. event with stamp 0
34 * occurred at start), with offset added.
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)
40 if (this->read_space() == 0) {
41 return 0;
44 T ev_time;
45 Evoral::EventType ev_type;
46 uint32_t ev_size;
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:
51 session frames----------------------------->
53 | | |
54 start_of_loop start end_of_loop
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).
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.
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.
74 frameoffset_t loop_offset = 0;
76 size_t count = 0;
78 const size_t prefix_size = sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t);
80 while (this->read_space() >= prefix_size) {
82 uint8_t peekbuf[prefix_size];
83 bool success;
85 success = this->peek (peekbuf, prefix_size);
86 /* this cannot fail, because we've already verified that there
87 is prefix_space to read
89 assert (success);
91 ev_time = *((T*) peekbuf);
92 ev_type = *((Evoral::EventType*)(peekbuf + sizeof (T)));
93 ev_size = *((uint32_t*)(peekbuf + sizeof(T) + sizeof (Evoral::EventType)));
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));
105 assert(ev_time >= start);
107 ev_time -= start;
108 ev_time += offset;
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.
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;
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
126 this->increment_read_ptr (prefix_size);
127 ev_time += loop_offset;
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
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;
144 /* lets see if we are going to be able to write this event into dst.
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;
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;
157 // write MIDI buffer contents
158 success = read_contents (ev_size, write_loc);
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,' ');
170 DEBUG_STR_APPEND(a,'\n');
171 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, DEBUG_STR(a).str());
173 #endif
175 if (success) {
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);
183 if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
184 write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F);
186 ++count;
187 } else {
188 cerr << "WARNING: error reading event contents from MIDI ring" << endl;
192 return count;
195 template<typename T>
196 void
197 MidiRingBuffer<T>::dump(ostream& str)
199 size_t rspace;
201 if ((rspace = this->read_space()) == 0) {
202 str << "MRB::dump: empty\n";
203 return;
206 T ev_time;
207 Evoral::EventType ev_type;
208 uint32_t ev_size;
210 RingBufferNPT<uint8_t>::rw_vector vec;
211 RingBufferNPT<uint8_t>::get_read_vector (&vec);
213 if (vec.len[0] == 0) {
214 return;
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;
222 uint8_t *buf = new uint8_t[vec.len[0] + vec.len[1]];
223 memcpy (buf, vec.buf[0], vec.len[0]);
225 if (vec.len[1]) {
226 memcpy (buf+vec.len[1], vec.buf[1], vec.len[1]);
229 uint8_t* data = buf;
230 const uint8_t* end = buf + vec.len[0] + vec.len[1];
232 while (data < end) {
234 memcpy (&ev_time, data, sizeof (T));
235 data += sizeof (T);
236 str << "\ttime " << ev_time;
238 if (data >= end) {
239 str << "(incomplete)\n ";
240 break;
243 memcpy (&ev_type, data, sizeof (ev_type));
244 data += sizeof (ev_type);
245 str << " type " << ev_type;
247 if (data >= end) {
248 str << "(incomplete)\n";
249 break;
252 memcpy (&ev_size, data, sizeof (ev_size));
253 data += sizeof (ev_size);
254 str << " size " << ev_size;
256 if (data >= end) {
257 str << "(incomplete)\n";
258 break;
261 for (uint32_t i = 0; i != ev_size && data < end; ++i) {
262 str << ' ' << hex << (int) data[i] << dec;
265 data += ev_size;
267 str << endl;
270 delete [] buf;
273 template class MidiRingBuffer<framepos_t>;