ae370cdef79c7e1b66b6a6a69bb3ae6b8a283e97
[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 //      , _owns_data(false)
39 {
40         if (capacity) {
41                 resize (_capacity);
42                 silence(_capacity);
43         }
44 }
45         
46 MidiBuffer::~MidiBuffer()
47 {
48         if (_events) {
49                 free(_events);
50         }
51         if (_data) {
52                 free(_data);
53         }
54 }
55
56 void
57 MidiBuffer::resize (size_t size)
58 {
59         assert(size > 0);
60
61         if (size < _capacity) {
62                 return;
63         }
64
65         if (_data) {
66                 free (_data);
67         }
68
69         if (_events) {
70                 free (_events);
71         }
72
73         _size = 0;
74         _capacity = size;
75
76 #ifdef NO_POSIX_MEMALIGN
77         _events = (Evoral::Event *) malloc(sizeof(Evoral::Event) * _capacity);
78         _data = (uint8_t *) malloc(sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
79 #else
80         posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(Evoral::Event) * _capacity);
81         posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
82 #endif  
83         assert(_data);
84         assert(_events);
85 }
86
87 void
88 MidiBuffer::copy(const MidiBuffer& copy)
89 {
90         assert(_capacity >= copy._capacity);
91         _size = 0;
92
93         for (size_t i = 0; i < copy.size(); ++i)
94                 push_back(copy[i]);
95 }
96
97
98 /** Read events from @a src starting at time @a offset into the START of this buffer, for
99  * time direction @a nframes.  Relative time, where 0 = start of buffer.
100  *
101  * Note that offset and nframes refer to sample time, NOT buffer offsets or event counts.
102  */
103 void
104 MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
105 {
106         assert(src.type() == DataType::MIDI);
107         assert(&src != this);
108
109         const MidiBuffer& msrc = (MidiBuffer&)src;
110         
111         assert(_capacity >= msrc.size());
112
113         if (offset == 0) {
114                 clear();
115                 assert(_size == 0);
116         }
117         
118         // FIXME: slow
119         for (size_t i=0; i < msrc.size(); ++i) {
120                 const Evoral::MIDIEvent& ev = msrc[i];
121                 if (ev.time() < offset)
122                     continue;
123                 if (ev.time() < (nframes + offset)) {
124                         //cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl;
125                         push_back(ev);
126                 } else {
127                         cerr << "MidiBuffer event out of range, " << ev.time() << endl;
128                 }
129         }
130
131         _silent = src.silent();
132 }
133
134
135 /** Push an event into the buffer.
136  *
137  * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
138  * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
139  * Realtime safe.
140  * @return false if operation failed (not enough room)
141  */
142 bool
143 MidiBuffer::push_back(const Evoral::MIDIEvent& ev)
144 {
145         if (_size == _capacity)
146                 return false;
147
148         uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
149
150         memcpy(write_loc, ev.buffer(), ev.size());
151         _events[_size] = ev;
152         _events[_size].set_buffer(ev.size(), write_loc, false);
153         ++_size;
154
155         //cerr << "MidiBuffer: pushed, size = " << _size << endl;
156
157         _silent = false;
158
159         return true;
160 }
161
162
163 /** Push an event into the buffer.
164  *
165  * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
166  * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
167  * Realtime safe.
168  * @return false if operation failed (not enough room)
169  */
170 bool
171 MidiBuffer::push_back(const jack_midi_event_t& ev)
172 {
173         if (_size == _capacity)
174                 return false;
175
176         uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
177
178         memcpy(write_loc, ev.buffer, ev.size);
179         _events[_size].time() = (double)ev.time;
180         _events[_size].set_buffer(ev.size, write_loc, false);
181         ++_size;
182
183         //cerr << "MidiBuffer: pushed, size = " << _size << endl;
184
185         _silent = false;
186
187         return true;
188 }
189
190
191 /** Reserve space for a new event in the buffer.
192  *
193  * This call is for copying MIDI directly into the buffer, the data location
194  * (of sufficient size to write \a size bytes) is returned, or 0 on failure.
195  * This call MUST be immediately followed by a write to the returned data
196  * location, or the buffer will be corrupted and very nasty things will happen.
197  */
198 uint8_t*
199 MidiBuffer::reserve(double time, size_t size)
200 {
201         if (size > MAX_EVENT_SIZE) {
202                 cerr << "WARNING: Failed to reserve " << size << " bytes for event";
203                 return 0;
204         }
205
206         if (_size == _capacity)
207                 return 0;
208
209         uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
210
211         _events[_size].time() = time;
212         _events[_size].set_buffer(size, write_loc, false);
213         ++_size;
214
215         //cerr << "MidiBuffer: reserved, size = " << _size << endl;
216
217         _silent = false;
218
219         return write_loc;
220 }
221
222
223 void
224 MidiBuffer::silence(nframes_t dur, nframes_t offset)
225 {
226         // FIXME use parameters
227         if (offset != 0)
228                 cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl;
229
230         memset(_events, 0, sizeof(Evoral::Event) * _capacity);
231         memset(_data, 0, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
232         _size = 0;
233         _silent = true;
234 }
235
236 bool
237 MidiBuffer::merge_in_place( const MidiBuffer &other )
238 {
239         if( other.size() == 0 )
240                 return true;
241
242         if( this->size() == 0 ) {
243                 copy( other );
244                 return true;
245         }
246
247         {
248                 MidiBuffer merge_buffer( 0 );
249                 Evoral::MIDIEvent onstack_events[_capacity];
250                 uint8_t   onstack_data[_capacity * MAX_EVENT_SIZE];
251                 merge_buffer._events = onstack_events;
252                 merge_buffer._data = onstack_data;
253                 merge_buffer._size = 0;
254
255                 bool retval = merge_buffer.merge( *this, other );
256
257                 copy( merge_buffer );
258
259                 // set pointers to zero again, so destructor
260                 // does not end in calling free() for memory
261                 // on the stack;
262                 merge_buffer._events = 0;
263                 merge_buffer._data = 0;
264
265                 return retval;
266         }
267 }
268
269 /** Clear, and merge \a a and \a b into this buffer.
270  *
271  * FIXME: This is slow.
272  *
273  * \return true if complete merge was successful
274  */
275 bool
276 MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
277 {
278         _size = 0;
279
280         // This is mostly the case :(
281         if( this == &a )
282             merge_in_place( b );
283
284         if( this == &b )
285             merge_in_place( a );
286
287         size_t a_index = 0;
288         size_t b_index = 0;
289         size_t count = a.size() + b.size();
290
291         while (count > 0) {
292                 
293                 if (size() == capacity()) {
294                         cerr << "WARNING: MIDI buffer overrun, events lost!" << endl;
295                         return false;
296                 }
297                 
298                 if (a_index == a.size()) {
299                         push_back(b[b_index]);
300                         ++b_index;
301                 } else if (b_index == b.size()) {
302                         push_back(a[a_index]);
303                         ++a_index;
304                 } else {
305                         const Evoral::MIDIEvent& a_ev = a[a_index];
306                         const Evoral::MIDIEvent& b_ev = b[b_index];
307
308                         if (a_ev.time() <= b_ev.time()) {
309                                 push_back(a_ev);
310                                 ++a_index;
311                         } else {
312                                 push_back(b_ev);
313                                 ++b_index;
314                         }
315                 }
316
317                 --count;
318         }
319
320         return true;
321 }
322