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"
32 /** Read a block of MIDI events from this buffer into a MidiBuffer.
34 * Timestamps of events returned are relative to start (i.e. event with stamp 0
35 * occurred at start), with offset added.
39 MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset, bool stop_on_overflow_in_dst)
41 if (this->read_space() == 0) {
46 Evoral::EventType ev_type;
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:
52 session frames----------------------------->
55 start_of_loop start end_of_loop
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).
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.
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
75 frameoffset_t loop_offset = 0;
79 const size_t prefix_size = sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t);
81 while (this->read_space() >= prefix_size) {
83 uint8_t peekbuf[prefix_size];
86 success = this->peek (peekbuf, prefix_size);
87 /* this cannot fail, because we've already verified that there
88 is prefix_space to read
92 ev_time = *((T*) peekbuf);
93 ev_type = *((Evoral::EventType*)(peekbuf + sizeof (T)));
94 ev_size = *((uint32_t*)(peekbuf + sizeof(T) + sizeof (Evoral::EventType)));
96 if (ev_time + loop_offset >= end) {
97 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 past end @ %2\n", ev_time, end));
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 this->increment_read_ptr (prefix_size);
102 this->increment_read_ptr (ev_size);
105 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 in range %2 .. %3\n", ev_time, start, end));
108 assert(ev_time >= start);
113 // This event marks a loop end (i.e. the next event's timestamp
114 // will be non-monotonic). Don't write it into the buffer - the
115 // significance of this event ends here.
117 if (ev_type == LoopEventType) {
118 assert (ev_size == sizeof (framepos_t));
119 framepos_t loop_start;
120 read_contents (ev_size, (uint8_t *) &loop_start);
121 loop_offset = ev_time - loop_start;
122 _tracker.resolve_notes (dst, ev_time);
126 /* we're good to go ahead and read the data now but since we
127 * have the prefix data already, just skip over that
129 this->increment_read_ptr (prefix_size);
130 ev_time += loop_offset;
133 success = this->peek (&status, sizeof(uint8_t));
134 assert(success); // If this failed, buffer is corrupt, all hope is lost
136 // Ignore event if it doesn't match channel filter
137 if (is_channel_event(status) && get_channel_mode() == FilterChannels) {
138 const uint8_t channel = status & 0x0F;
139 if (!(get_channel_mask() & (1L << channel))) {
140 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB skipping event (%3 bytes) due to channel mask (mask = %1 chn = %2)\n",
141 get_channel_mask(), (int) channel, ev_size));
142 this->increment_read_ptr (ev_size); // Advance read pointer to next event
147 /* lets see if we are going to be able to write this event into dst.
149 uint8_t* write_loc = dst.reserve (ev_time, ev_size);
150 if (write_loc == 0) {
151 if (stop_on_overflow_in_dst) {
152 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MidiRingBuffer: overflow in destination MIDI buffer, stopped after %1 events\n", count));
155 error << "MRB: Unable to reserve space in buffer, event skipped" << endmsg;
156 this->increment_read_ptr (ev_size); // Advance read pointer to next event
160 // write MIDI buffer contents
161 success = read_contents (ev_size, write_loc);
164 if (DEBUG::MidiDiskstreamIO && PBD::debug_bits) {
166 DEBUG_STR_APPEND(a, string_compose ("wrote MidiEvent to Buffer (time=%1, start=%2 offset=%3)", ev_time, start, offset));
167 for (size_t i=0; i < ev_size; ++i) {
168 DEBUG_STR_APPEND(a,hex);
169 DEBUG_STR_APPEND(a,"0x");
170 DEBUG_STR_APPEND(a,(int)write_loc[i]);
171 DEBUG_STR_APPEND(a,' ');
173 DEBUG_STR_APPEND(a,'\n');
174 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, DEBUG_STR(a).str());
180 if (is_note_on(write_loc[0]) ) {
181 _tracker.add (write_loc[1], write_loc[0] & 0xf);
182 } else if (is_note_off(write_loc[0])) {
183 _tracker.remove (write_loc[1], write_loc[0] & 0xf);
186 if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
187 write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F);
191 cerr << "WARNING: error reading event contents from MIDI ring" << endl;
200 MidiRingBuffer<T>::flush (framepos_t start, framepos_t end)
202 const size_t prefix_size = sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t);
204 while (this->read_space() >= prefix_size) {
205 uint8_t peekbuf[prefix_size];
210 success = this->peek (peekbuf, prefix_size);
211 /* this cannot fail, because we've already verified that there
212 is prefix_space to read
216 ev_time = *((T*) peekbuf);
218 if (ev_time >= end) {
222 ev_size = *((uint32_t*)(peekbuf + sizeof(T) + sizeof (Evoral::EventType)));
223 this->increment_read_ptr (prefix_size);
224 this->increment_read_ptr (ev_size);
230 MidiRingBuffer<T>::dump(ostream& str)
234 if ((rspace = this->read_space()) == 0) {
235 str << "MRB::dump: empty\n";
240 Evoral::EventType ev_type;
243 RingBufferNPT<uint8_t>::rw_vector vec;
244 RingBufferNPT<uint8_t>::get_read_vector (&vec);
246 if (vec.len[0] == 0) {
250 str << this << ": Dump size = " << vec.len[0] + vec.len[1]
251 << " r@ " << RingBufferNPT<uint8_t>::get_read_ptr()
252 << " w@" << RingBufferNPT<uint8_t>::get_write_ptr() << endl;
255 uint8_t *buf = new uint8_t[vec.len[0] + vec.len[1]];
256 memcpy (buf, vec.buf[0], vec.len[0]);
259 memcpy (buf+vec.len[1], vec.buf[1], vec.len[1]);
263 const uint8_t* end = buf + vec.len[0] + vec.len[1];
267 memcpy (&ev_time, data, sizeof (T));
269 str << "\ttime " << ev_time;
272 str << "(incomplete)\n ";
276 memcpy (&ev_type, data, sizeof (ev_type));
277 data += sizeof (ev_type);
278 str << " type " << ev_type;
281 str << "(incomplete)\n";
285 memcpy (&ev_size, data, sizeof (ev_size));
286 data += sizeof (ev_size);
287 str << " size " << ev_size;
290 str << "(incomplete)\n";
294 for (uint32_t i = 0; i != ev_size && data < end; ++i) {
295 str << ' ' << hex << (int) data[i] << dec;
308 MidiRingBuffer<T>::reset_tracker ()
313 template class MidiRingBuffer<framepos_t>;
315 } // namespace ARDOUR