remove unused SHUTTLE_FRACT constant
[ardour2.git] / libs / ardour / midi_ring_buffer.cc
blob850313cc12c7c83177d85c329bc96acc2790967f
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"
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"
26 using namespace std;
27 using namespace ARDOUR;
28 using namespace PBD;
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.
35 template<typename T>
36 size_t
37 MidiRingBuffer<T>::read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset)
39 if (this->read_space() == 0) {
40 return 0;
43 T ev_time;
44 Evoral::EventType ev_type;
45 uint32_t ev_size;
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----------------------------->
52 | | |
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
70 E_t + offset - start.
73 frameoffset_t loop_offset = 0;
75 size_t count = 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));
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));
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;
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;
104 continue;
107 ev_time += loop_offset;
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
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;
123 assert(ev_time >= start);
125 ev_time -= start;
126 ev_time += offset;
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;
136 // write MIDI buffer contents
137 success = read_contents (ev_size, write_loc);
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,' ');
149 DEBUG_STR_APPEND(a,'\n');
150 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, DEBUG_STR(a).str());
152 #endif
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);
158 ++count;
159 } else {
160 cerr << "WARNING: error reading event contents from MIDI ring" << endl;
164 return count;
167 template<typename T>
168 void
169 MidiRingBuffer<T>::dump(ostream& str)
171 size_t rspace;
173 if ((rspace = this->read_space()) == 0) {
174 str << "MRB::dump: empty\n";
175 return;
178 T ev_time;
179 Evoral::EventType ev_type;
180 uint32_t ev_size;
182 RingBufferNPT<uint8_t>::rw_vector vec;
183 RingBufferNPT<uint8_t>::get_read_vector (&vec);
185 if (vec.len[0] == 0) {
186 return;
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;
194 uint8_t *buf = new uint8_t[vec.len[0] + vec.len[1]];
195 memcpy (buf, vec.buf[0], vec.len[0]);
197 if (vec.len[1]) {
198 memcpy (buf+vec.len[1], vec.buf[1], vec.len[1]);
201 uint8_t* data = buf;
202 const uint8_t* end = buf + vec.len[0] + vec.len[1];
204 while (data < end) {
206 memcpy (&ev_time, data, sizeof (T));
207 data += sizeof (T);
208 str << "\ttime " << ev_time;
210 if (data >= end) {
211 str << "(incomplete)\n ";
212 break;
215 memcpy (&ev_type, data, sizeof (ev_type));
216 data += sizeof (ev_type);
217 str << " type " << ev_type;
219 if (data >= end) {
220 str << "(incomplete)\n";
221 break;
224 memcpy (&ev_size, data, sizeof (ev_size));
225 data += sizeof (ev_size);
226 str << " size " << ev_size;
228 if (data >= end) {
229 str << "(incomplete)\n";
230 break;
233 for (uint32_t i = 0; i != ev_size && data < end; ++i) {
234 str << ' ' << hex << (int) data[i] << dec;
237 data += ev_size;
239 str << endl;
242 delete [] buf;
245 template class MidiRingBuffer<framepos_t>;