2 Copyright (C) 2006-2007 Paul Davis
3 Author: David Robillard
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)
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
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.
22 #include "pbd/malign.h"
23 #include "pbd/compose.h"
24 #include "pbd/debug.h"
26 #include "ardour/debug.h"
27 #include "ardour/midi_buffer.h"
30 using namespace ARDOUR
;
33 // FIXME: mirroring for MIDI buffers?
34 MidiBuffer::MidiBuffer(size_t capacity
)
35 : Buffer(DataType::MIDI
, capacity
)
44 MidiBuffer::~MidiBuffer()
50 MidiBuffer::resize(size_t size
)
54 if (size
< _capacity
) {
62 cache_aligned_malloc ((void**) &_data
, _capacity
);
68 MidiBuffer::copy(const MidiBuffer
& copy
)
70 assert(_capacity
>= copy
._size
);
72 memcpy(_data
, copy
._data
, copy
._size
);
76 /** Read events from @a src starting at time @a offset into the START of this buffer, for
77 * time duration @a nframes. Relative time, where 0 = start of buffer.
79 * Note that offset and nframes refer to sample time, NOT buffer offsets or event counts.
82 MidiBuffer::read_from (const Buffer
& src
, framecnt_t nframes
, framecnt_t dst_offset
, framecnt_t src_offset
)
84 assert (src
.type() == DataType::MIDI
);
85 assert (&src
!= this);
87 const MidiBuffer
& msrc
= (MidiBuffer
&) src
;
89 assert (_capacity
>= msrc
.size());
91 if (dst_offset
== 0) {
96 /* XXX use dst_offset somehow */
98 for (MidiBuffer::const_iterator i
= msrc
.begin(); i
!= msrc
.end(); ++i
) {
99 const Evoral::MIDIEvent
<TimeType
> ev(*i
, false);
100 if (ev
.time() >= src_offset
&& ev
.time() < (nframes
+src_offset
)) {
103 cerr
<< "MIDI event @ " << ev
.time() << " skipped, not within range "
104 << src_offset
<< " .. " << (nframes
+ src_offset
) << endl
;
108 _silent
= src
.silent();
112 MidiBuffer::merge_from (const Buffer
& src
, framecnt_t
/*nframes*/, framecnt_t
/*dst_offset*/, framecnt_t
/*src_offset*/)
114 const MidiBuffer
* mbuf
= dynamic_cast<const MidiBuffer
*>(&src
);
116 assert (mbuf
!= this);
118 /* XXX use nframes, and possible offsets */
119 merge_in_place (*mbuf
);
122 /** Push an event into the buffer.
124 * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
125 * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
127 * @return false if operation failed (not enough room)
130 MidiBuffer::push_back(const Evoral::MIDIEvent
<TimeType
>& ev
)
132 const size_t stamp_size
= sizeof(TimeType
);
133 /*cerr << "MidiBuffer: pushing event @ " << ev.time()
134 << " size = " << ev.size() << endl;*/
136 if (_size
+ stamp_size
+ ev
.size() >= _capacity
) {
137 cerr
<< "MidiBuffer::push_back failed (buffer is full)" << endl
;
141 if (!Evoral::midi_event_is_valid(ev
.buffer(), ev
.size())) {
142 cerr
<< "WARNING: MidiBuffer ignoring illegal MIDI event" << endl
;
146 push_back(ev
.time(), ev
.size(), ev
.buffer());
152 /** Push an event into the buffer.
153 * @return false if operation failed (not enough room)
156 MidiBuffer::push_back(TimeType time
, size_t size
, const uint8_t* data
)
158 const size_t stamp_size
= sizeof(TimeType
);
161 if (DEBUG::MidiIO
& PBD::debug_bits
) {
163 DEBUG_STR_APPEND(a
, string_compose ("midibuffer %1 push event @ %2 sz %3 ", this, time
, size
));
164 for (size_t i
=0; i
< size
; ++i
) {
165 DEBUG_STR_APPEND(a
,hex
);
166 DEBUG_STR_APPEND(a
,"0x");
167 DEBUG_STR_APPEND(a
,(int)data
[i
]);
168 DEBUG_STR_APPEND(a
,' ');
170 DEBUG_STR_APPEND(a
,'\n');
171 DEBUG_TRACE (DEBUG::MidiIO
, DEBUG_STR(a
).str());
175 if (_size
+ stamp_size
+ size
>= _capacity
) {
176 cerr
<< "MidiBuffer::push_back failed (buffer is full)" << endl
;
180 if (!Evoral::midi_event_is_valid(data
, size
)) {
181 cerr
<< "WARNING: MidiBuffer ignoring illegal MIDI event" << endl
;
185 uint8_t* const write_loc
= _data
+ _size
;
186 *((TimeType
*)write_loc
) = time
;
187 memcpy(write_loc
+ stamp_size
, data
, size
);
189 _size
+= stamp_size
+ size
;
196 /** Push an event into the buffer.
198 * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
199 * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
201 * @return false if operation failed (not enough room)
204 MidiBuffer::push_back(const jack_midi_event_t
& ev
)
206 const size_t stamp_size
= sizeof(TimeType
);
207 if (_size
+ stamp_size
+ ev
.size
>= _capacity
) {
208 cerr
<< "MidiBuffer::push_back failed (buffer is full)" << endl
;
212 if (!Evoral::midi_event_is_valid(ev
.buffer
, ev
.size
)) {
213 cerr
<< "WARNING: MidiBuffer ignoring illegal MIDI event" << endl
;
217 uint8_t* const write_loc
= _data
+ _size
;
218 *((TimeType
*)write_loc
) = ev
.time
;
219 memcpy(write_loc
+ stamp_size
, ev
.buffer
, ev
.size
);
221 _size
+= stamp_size
+ ev
.size
;
228 /** Reserve space for a new event in the buffer.
230 * This call is for copying MIDI directly into the buffer, the data location
231 * (of sufficient size to write \a size bytes) is returned, or 0 on failure.
232 * This call MUST be immediately followed by a write to the returned data
233 * location, or the buffer will be corrupted and very nasty things will happen.
236 MidiBuffer::reserve(TimeType time
, size_t size
)
238 const size_t stamp_size
= sizeof(TimeType
);
239 if (_size
+ stamp_size
+ size
>= _capacity
) {
244 uint8_t* write_loc
= _data
+ _size
;
245 *((TimeType
*)write_loc
) = time
;
247 // move write_loc to begin of MIDI buffer data to write to
248 write_loc
+= stamp_size
;
250 _size
+= stamp_size
+ size
;
258 MidiBuffer::silence (framecnt_t
/*nframes*/, framecnt_t
/*offset*/)
260 /* XXX iterate over existing events, find all in range given by offset & nframes,
268 /** Merge \a other into this buffer. Realtime safe. */
270 MidiBuffer::merge_in_place(const MidiBuffer
&other
)
272 if (other
.size() == 0) {
281 if (_size
+ other
.size() > _capacity
) {
282 cerr
<< "MidiBuffer::merge failed (no space)" << endl
;
287 size_t test_orig_us_size
= _size
;
288 size_t test_orig_them_size
= other
._size
;
289 TimeType test_time
= 0;
290 size_t test_us_count
= 0;
291 size_t test_them_count
= 0;
292 for (iterator i
= begin(); i
!= end(); ++i
) {
293 assert(Evoral::midi_event_is_valid((*i
).buffer(), (*i
).size()));
294 assert((*i
).time() >= test_time
);
295 test_time
= (*i
).time();
299 for (const_iterator i
= other
.begin(); i
!= other
.end(); ++i
) {
300 assert(Evoral::midi_event_is_valid((*i
).buffer(), (*i
).size()));
301 assert((*i
).time() >= test_time
);
302 test_time
= (*i
).time();
307 const_iterator them
= other
.begin();
308 iterator us
= begin();
310 while (them
!= other
.end()) {
315 /* gather up total size of events that are earlier than
316 the event referenced by "us"
319 while (them
!= other
.end() && (*them
).time() <= (*us
).time()) {
323 sz
+= sizeof (TimeType
) + (*them
).size();
329 cerr
<< "us @ " << (*us
).time() << endl
;
330 if (them
!= other
.end())
331 cerr
<< "them @ " << (*them
).time() << endl
;
337 memmove (_data
+ us
.offset
+ sz
, _data
+ us
.offset
, _size
- us
.offset
);
340 assert(_size
<= _capacity
);
341 /* insert new stuff */
342 memcpy (_data
+ us
.offset
, other
._data
+ src
, sz
);
343 /* update iterator to our own events. this is a miserable hack */
347 /* advance past our own events to get to the correct insertion
348 point for the next event(s) from "other"
351 while (us
!= end() && (*us
).time() < (*them
).time()) {
356 if (!(us
!= end())) {
357 /* just append the rest of other */
358 memcpy (_data
+ us
.offset
, other
._data
+ them
.offset
, other
._size
- them
.offset
);
359 _size
+= other
._size
- them
.offset
;
365 assert(_size
== test_orig_us_size
+ test_orig_them_size
);
366 size_t test_final_count
= 0;
368 for (iterator i
= begin(); i
!= end(); ++i
) {
369 // cerr << "CHECK " << test_final_count << " / " << test_us_count + test_them_count << endl;
370 assert(Evoral::midi_event_is_valid((*i
).buffer(), (*i
).size()));
371 assert((*i
).time() >= test_time
);
372 test_time
= (*i
).time();
375 assert(test_final_count
= test_us_count
+ test_them_count
);
381 /** Clear, and merge \a a and \a b into this buffer.
383 * \return true if complete merge was successful
386 MidiBuffer::merge(const MidiBuffer
& a
, const MidiBuffer
& b
)
391 return merge_in_place(b
);
392 } else if (this == &b
) {
393 return merge_in_place(a
);
396 const_iterator ai
= a
.begin();
397 const_iterator bi
= b
.begin();
399 resize(a
.size() + b
.size());
400 while (ai
!= a
.end() && bi
!= b
.end()) {
401 if ((*ai
).time() < (*bi
).time()) {
402 memcpy(_data
+ _size
, (*ai
).buffer(), (*ai
).size());
403 _size
+= (*ai
).size();
406 memcpy(_data
+ _size
, (*bi
).buffer(), (*bi
).size());
407 _size
+= (*bi
).size();
412 while (ai
!= a
.end()) {
413 memcpy(_data
+ _size
, (*ai
).buffer(), (*ai
).size());
414 _size
+= (*ai
).size();
418 while (bi
!= b
.end()) {
419 memcpy(_data
+ _size
, (*bi
).buffer(), (*bi
).size());
420 _size
+= (*bi
).size();