a69b3539950f865c07dbaf6bf904d4c479c54d14
[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 #if 0
168 template<typename T>
169 void
170 MidiRingBuffer<T>::dump(ostream& str)
171 {
172         size_t rspace;
173
174         if ((rspace = this->read_space()) == 0) {
175                 str << "MRB::dump: empty\n";
176                 return;
177         }
178
179         T                 ev_time;
180         Evoral::EventType ev_type;
181         uint32_t          ev_size;
182         size_t read_ptr = g_atomic_int_get (&this->_read_ptr);
183
184         str << "Dump @ " << read_ptr << endl;
185
186         while (1) {
187                 uint8_t* wp;
188                 uint8_t* data;
189                 size_t write_ptr;
190
191 #define space(r,w) ((w > r) ? (w - r) : ((w - r + this->_size) % this->_size))
192
193                 write_ptr  = g_atomic_int_get (&this->_write_ptr);
194                 if (space (read_ptr, write_ptr) < sizeof (T)) {
195                         break;
196                 }
197
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;
202
203                 write_ptr  = g_atomic_int_get (&this->_write_ptr);
204                 if (space (read_ptr, write_ptr) < sizeof (ev_type)) {
205                         break;
206                 }
207
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;
212
213                 write_ptr  = g_atomic_int_get (&this->_write_ptr);
214                 if (space (read_ptr, write_ptr) < sizeof (ev_size)) {
215                         str << "!OUT!\n";
216                         break;
217                 }
218
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;
223
224                 write_ptr  = g_atomic_int_get (&this->_write_ptr);
225                 if (space (read_ptr, write_ptr) < ev_size) {
226                         str << "!OUT!\n";
227                         break;
228                 }
229
230                 data = new uint8_t[ev_size];
231
232                 wp = &this->_buf[read_ptr];
233                 memcpy (data, wp, ev_size);
234                 read_ptr = (read_ptr + ev_size) % this->_size;
235
236                 for (uint32_t i = 0; i != ev_size; ++i) {
237                         str << ' ' << hex << (int) data[i] << dec;
238                 }
239
240                 str << endl;
241
242                 delete [] data;
243         }
244 }
245 #endif
246
247 template class MidiRingBuffer<framepos_t>;
248