Templateify MidiBuffer iterators (avoid code duplication since they're about to get...
[ardour.git] / libs / ardour / midi_buffer.cc
1 /*
2     Copyright (C) 2006-2007 Paul Davis 
3         Author: Dave Robillard
4     
5     This program is free software; you can redistribute it and/or modify it
6     under the terms of the GNU General Public License as published by the Free
7     Software Foundation; either version 2 of the License, or (at your option)
8     any later version.
9     
10     This program is distributed in the hope that it will be useful, but WITHOUT
11     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13     for more details.
14     
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <iostream>
21 #include <ardour/midi_buffer.h>
22
23 #ifdef __x86_64__
24 static const int CPU_CACHE_ALIGN = 64;
25 #else
26 static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */
27 #endif
28
29 using namespace std;
30 using namespace ARDOUR;
31
32
33 // FIXME: mirroring for MIDI buffers?
34 MidiBuffer::MidiBuffer(size_t capacity)
35         : Buffer(DataType::MIDI, capacity)
36         , _events(0)
37         , _data(0)
38 {
39         if (capacity) {
40                 resize(_capacity);
41                 silence(_capacity);
42         }
43 }
44         
45 MidiBuffer::~MidiBuffer()
46 {
47         if (_events) {
48                 free(_events);
49         }
50         if (_data) {
51                 free(_data);
52         }
53 }
54
55 void
56 MidiBuffer::resize(size_t size)
57 {
58         assert(size > 0);
59
60         if (size < _capacity) {
61                 return;
62         }
63
64         free(_data);
65         free(_events);
66
67         _size = 0;
68         _capacity = size;
69
70 #ifdef NO_POSIX_MEMALIGN
71         _events = (Evoral::MIDIEvent *) malloc(sizeof(Evoral::MIDIEvent) * _capacity);
72         _data = (uint8_t *) malloc(sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
73 #else
74         posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(Evoral::Event) * _capacity);
75         posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
76 #endif  
77         assert(_data);
78         assert(_events);
79 }
80
81 void
82 MidiBuffer::copy(const MidiBuffer& copy)
83 {
84         assert(_capacity >= copy._capacity);
85         _size = 0;
86
87         for (size_t i = 0; i < copy.size(); ++i)
88                 push_back(copy[i]);
89 }
90
91
92 /** Read events from @a src starting at time @a offset into the START of this buffer, for
93  * time duration @a nframes.  Relative time, where 0 = start of buffer.
94  *
95  * Note that offset and nframes refer to sample time, NOT buffer offsets or event counts.
96  */
97 void
98 MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
99 {
100         assert(src.type() == DataType::MIDI);
101         assert(&src != this);
102
103         const MidiBuffer& msrc = (MidiBuffer&)src;
104         
105         assert(_capacity >= msrc.size());
106
107         if (offset == 0) {
108                 clear();
109                 assert(_size == 0);
110         }
111         
112         // FIXME: slow
113         for (size_t i = 0; i < msrc.size(); ++i) {
114                 const Evoral::MIDIEvent& ev = msrc[i];
115                 //cout << "MidiBuffer::read_from event type: " << int(ev.type())
116                 //              << " time: " << ev.time() << " buffer size: " << _size << endl;
117                 if (ev.time() < offset) {
118                         //cout << "MidiBuffer::read_from skipped event before " << offset << endl;
119                 } else if (ev.time() < (nframes + offset)) {
120                         //cout << "MidiBuffer::read_from appending event" << endl;
121                         push_back(ev);
122                 } else {
123                         //cerr << "MidiBuffer::read_from skipped event after "
124                         //      << nframes << " + " << offset << endl;
125                 }
126         }
127
128         _silent = src.silent();
129 }
130
131
132 /** Push an event into the buffer.
133  *
134  * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
135  * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
136  * Realtime safe.
137  * @return false if operation failed (not enough room)
138  */
139 bool
140 MidiBuffer::push_back(const Evoral::MIDIEvent& ev)
141 {
142         if (_size == _capacity) {
143                 cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
144                 return false;
145         }
146
147         uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
148
149         memcpy(write_loc, ev.buffer(), ev.size());
150         _events[_size] = ev;
151         _events[_size].set_buffer(ev.size(), write_loc, false);
152
153         /*cerr << "MidiBuffer: pushed @ " << _events[_size].time()
154                 << " size = " << _size << endl;
155         for (size_t i = 0; i < _events[_size].size(); ++i) {
156                 printf("%X ", _events[_size].buffer()[i]);
157         }
158         printf("\n");*/
159
160         ++_size;
161         _silent = false;
162
163         return true;
164 }
165
166
167 /** Push an event into the buffer.
168  *
169  * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
170  * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
171  * Realtime safe.
172  * @return false if operation failed (not enough room)
173  */
174 bool
175 MidiBuffer::push_back(const jack_midi_event_t& ev)
176 {
177         if (_size == _capacity) {
178                 cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
179                 return false;
180         }
181
182         uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
183
184         memcpy(write_loc, ev.buffer, ev.size);
185         _events[_size].time() = (double)ev.time;
186         _events[_size].set_buffer(ev.size, write_loc, false);
187
188         /*cerr << "MidiBuffer: pushed @ " << _events[_size].time()
189                 << " size = " << _size << endl;
190         for (size_t i = 0; i < _events[_size].size(); ++i) {
191                 printf("%X ", _events[_size].buffer()[i]);
192         }
193         printf("\n");*/
194         
195         ++_size;
196         _silent = false;
197
198         return true;
199 }
200
201
202 /** Reserve space for a new event in the buffer.
203  *
204  * This call is for copying MIDI directly into the buffer, the data location
205  * (of sufficient size to write \a size bytes) is returned, or 0 on failure.
206  * This call MUST be immediately followed by a write to the returned data
207  * location, or the buffer will be corrupted and very nasty things will happen.
208  */
209 uint8_t*
210 MidiBuffer::reserve(double time, size_t size)
211 {
212         if (size > MAX_EVENT_SIZE) {
213                 cerr << "WARNING: Failed to reserve " << size << " bytes for event";
214                 return 0;
215         }
216
217         if (_size == _capacity) {
218                 return 0;
219         }
220
221         uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
222
223         _events[_size].time() = time;
224         _events[_size].set_buffer(size, write_loc, false);
225         ++_size;
226
227         //cerr << "MidiBuffer: reserved, size = " << _size << endl;
228
229         _silent = false;
230
231         return write_loc;
232 }
233
234
235 void
236 MidiBuffer::silence(nframes_t dur, nframes_t offset)
237 {
238         // FIXME use parameters
239         if (offset != 0)
240                 cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl;
241
242         memset(_events, 0, sizeof(Evoral::Event) * _capacity);
243         memset(_data, 0, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
244         _size = 0;
245         _silent = true;
246 }
247
248 bool
249 MidiBuffer::merge_in_place(const MidiBuffer &other)
250 {
251         if (other.size() == 0) {
252                 return true;
253         }
254
255         if (this->size() == 0) {
256                 copy(other);
257                 return true;
258         }
259
260         {
261                 MidiBuffer merge_buffer(0);
262                 Evoral::MIDIEvent onstack_events[_capacity];
263                 uint8_t onstack_data[_capacity * MAX_EVENT_SIZE];
264                 merge_buffer._events = onstack_events;
265                 merge_buffer._data = onstack_data;
266                 merge_buffer._size = 0;
267
268                 bool retval = merge_buffer.merge(*this, other);
269
270                 copy(merge_buffer);
271
272                 // set pointers to zero again, so destructor
273                 // does not end in calling free() for memory
274                 // on the stack;
275                 merge_buffer._events = 0;
276                 merge_buffer._data = 0;
277
278                 return retval;
279         }
280 }
281
282 /** Clear, and merge \a a and \a b into this buffer.
283  *
284  * FIXME: This is slow.
285  *
286  * \return true if complete merge was successful
287  */
288 bool
289 MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
290 {
291         _size = 0;
292         
293         if (this == &a) {
294             merge_in_place(b);
295         }
296
297         if (this == &b) {
298             merge_in_place(a);
299         }
300
301         size_t a_index = 0;
302         size_t b_index = 0;
303         size_t count = a.size() + b.size();
304
305         while (count > 0) {
306                 
307                 if (size() == capacity()) {
308                         cerr << "WARNING: MIDI buffer overrun, events lost!" << endl;
309                         return false;
310                 }
311                 
312                 if (a_index == a.size()) {
313                         push_back(b[b_index]);
314                         ++b_index;
315                 } else if (b_index == b.size()) {
316                         push_back(a[a_index]);
317                         ++a_index;
318                 } else {
319                         const Evoral::MIDIEvent& a_ev = a[a_index];
320                         const Evoral::MIDIEvent& b_ev = b[b_index];
321
322                         if (a_ev.time() <= b_ev.time()) {
323                                 push_back(a_ev);
324                                 ++a_index;
325                         } else {
326                                 push_back(b_ev);
327                                 ++b_index;
328                         }
329                 }
330
331                 --count;
332         }
333
334         return true;
335 }
336