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"
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"
27 using namespace ARDOUR;
30 /** Read a block of MIDI events from this buffer into a MidiBuffer.
32 * Timestamps of events returned are relative to start (i.e. event with stamp 0
33 * occurred at start), with offset added.
37 MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset)
39 if (this->read_space() == 0) {
44 Evoral::EventType ev_type;
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:
50 session frames----------------------------->
53 start_of_loop start end_of_loop
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).
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.
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
73 frameoffset_t loop_offset = 0;
77 while (this->read_space() >= sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t)) {
79 this->peek ((uint8_t*) &ev_time, sizeof (T));
81 if (ev_time + loop_offset >= end) {
82 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 past end @ %2\n", ev_time, end));
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));
88 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 in range %2 .. %3\n", ev_time, start, end));
91 bool success = read_prefix(&ev_time, &ev_type, &ev_size);
93 cerr << "WARNING: error reading event prefix from MIDI ring" << endl;
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);
103 loop_offset = ev_time - loop_start;
107 ev_time += loop_offset;
110 success = this->peek (&status, sizeof(uint8_t));
111 assert(success); // If this failed, buffer is corrupt, all hope is lost
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
123 assert(ev_time >= start);
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
136 // write MIDI buffer contents
137 success = read_contents (ev_size, write_loc);
140 if (DEBUG::MidiDiskstreamIO && PBD::debug_bits) {
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,' ');
149 DEBUG_STR_APPEND(a,'\n');
150 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, DEBUG_STR(a).str());
155 if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
156 write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F);
160 cerr << "WARNING: error reading event contents from MIDI ring" << endl;
170 MidiRingBuffer<T>::dump(ostream& str)
174 if ((rspace = this->read_space()) == 0) {
175 str << "MRB::dump: empty\n";
180 Evoral::EventType ev_type;
182 size_t read_ptr = g_atomic_int_get (&this->_read_ptr);
184 str << "Dump @ " << read_ptr << endl;
191 #define space(r,w) ((w > r) ? (w - r) : ((w - r + this->_size) % this->_size))
193 write_ptr = g_atomic_int_get (&this->_write_ptr);
194 if (space (read_ptr, write_ptr) < sizeof (T)) {
198 wp = &this->_buf[read_ptr];
199 memcpy (&ev_time, wp, sizeof (T));
200 read_ptr = (read_ptr + sizeof (T)) % this->_size;
201 str << "time " << ev_time;
203 write_ptr = g_atomic_int_get (&this->_write_ptr);
204 if (space (read_ptr, write_ptr) < sizeof (ev_type)) {
208 wp = &this->_buf[read_ptr];
209 memcpy (&ev_type, wp, sizeof (ev_type));
210 read_ptr = (read_ptr + sizeof (ev_type)) % this->_size;
211 str << " type " << ev_type;
213 write_ptr = g_atomic_int_get (&this->_write_ptr);
214 if (space (read_ptr, write_ptr) < sizeof (ev_size)) {
219 wp = &this->_buf[read_ptr];
220 memcpy (&ev_size, wp, sizeof (ev_size));
221 read_ptr = (read_ptr + sizeof (ev_size)) % this->_size;
222 str << " size " << ev_size;
224 write_ptr = g_atomic_int_get (&this->_write_ptr);
225 if (space (read_ptr, write_ptr) < ev_size) {
230 data = new uint8_t[ev_size];
232 wp = &this->_buf[read_ptr];
233 memcpy (data, wp, ev_size);
234 read_ptr = (read_ptr + ev_size) % this->_size;
236 for (uint32_t i = 0; i != ev_size; ++i) {
237 str << ' ' << hex << (int) data[i] << dec;
247 template class MidiRingBuffer<framepos_t>;