7960edf5651c3113ec1d6bde57dddd989486e504
[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         , _size(0)
37         , _data(0)
38 {
39         if (capacity) {
40                 resize(_capacity);
41                 silence(_capacity);
42         }
43 }
44         
45 MidiBuffer::~MidiBuffer()
46 {
47         free(_data);
48 }
49
50 void
51 MidiBuffer::resize(size_t size)
52 {
53         assert(size > 0);
54
55         if (size < _capacity) {
56                 return;
57         }
58
59         free(_data);
60
61         _size = 0;
62         _capacity = size;
63
64 #ifdef NO_POSIX_MEMALIGN
65         _data = (uint8_t*)malloc(_capacity);
66 #else
67         posix_memalign((void**)&_data, CPU_CACHE_ALIGN, _capacity);
68 #endif  
69         assert(_data);
70 }
71
72 void
73 MidiBuffer::copy(const MidiBuffer& copy)
74 {
75         assert(_capacity >= copy._size);
76         _size = copy._size;
77         memcpy(_data, copy._data, copy._size);
78 }
79
80
81 /** Read events from @a src starting at time @a offset into the START of this buffer, for
82  * time duration @a nframes.  Relative time, where 0 = start of buffer.
83  *
84  * Note that offset and nframes refer to sample time, NOT buffer offsets or event counts.
85  */
86 void
87 MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
88 {
89         assert(src.type() == DataType::MIDI);
90         assert(&src != this);
91
92         const MidiBuffer& msrc = (MidiBuffer&)src;
93         
94         assert(_capacity >= msrc.size());
95
96         if (offset == 0) {
97                 clear();
98                 assert(_size == 0);
99         }
100         
101         for (MidiBuffer::const_iterator i = msrc.begin(); i != msrc.end(); ++i) {
102                 const Evoral::MIDIEvent<TimeType> ev(*i, false);
103                 /*cout << this << " MidiBuffer::read_from event type: " << int(ev.type())
104                                 << " time: " << ev.time() << " size: " << ev.size()
105                                 << " status: " << (int)*ev.buffer() << " buffer size: " << _size << endl;*/
106                 if (ev.time() < offset) {
107                         //cout << "MidiBuffer::read_from skipped event before " << offset << endl;
108                 } else if (ev.time() < (nframes + offset)) {
109                         //cout << "MidiBuffer::read_from appending event" << endl;
110                         push_back(ev);
111                 } else {
112                         //cerr << "MidiBuffer::read_from skipped event after "
113                         //      << nframes << " + " << offset << endl;
114                 }
115         }
116
117         _silent = src.silent();
118 }
119
120
121 /** Push an event into the buffer.
122  *
123  * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
124  * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
125  * Realtime safe.
126  * @return false if operation failed (not enough room)
127  */
128 bool
129 MidiBuffer::push_back(const Evoral::MIDIEvent<TimeType>& ev)
130 {
131         const size_t stamp_size = sizeof(TimeType);
132         /*cerr << "MidiBuffer: pushing event " << " size: " << _size 
133             << " event size: " << ev.size() 
134             << " capacity: " << _capacity 
135             << " stamp size: " << stamp_size << " \n";*/
136         
137         if (_size + stamp_size + ev.size() >= _capacity) {
138                 cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
139                 return false;
140         }
141
142         if (!Evoral::midi_event_is_valid(ev.buffer(), ev.size())) {
143                 cerr << "WARNING: MidiBuffer ignoring illegal MIDI event" << endl;
144                 return false;
145         }
146
147         uint8_t* const write_loc = _data + _size;
148         *((TimeType*)write_loc) = ev.time();
149         memcpy(write_loc + stamp_size, ev.buffer(), ev.size());
150
151         _size += stamp_size + ev.size();
152         _silent = false;
153         
154         return true;
155 }
156
157
158 /** Push an event into the buffer.
159  *
160  * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
161  * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
162  * Realtime safe.
163  * @return false if operation failed (not enough room)
164  */
165 bool
166 MidiBuffer::push_back(const jack_midi_event_t& ev)
167 {
168         const size_t stamp_size = sizeof(TimeType);
169         if (_size + stamp_size + ev.size >= _capacity) {
170                 cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
171                 return false;
172         }
173         
174         if (!Evoral::midi_event_is_valid(ev.buffer, ev.size)) {
175                 cerr << "WARNING: MidiBuffer ignoring illegal MIDI event" << endl;
176                 return false;
177         }
178
179         uint8_t* const write_loc = _data + _size;
180         *((TimeType*)write_loc) = ev.time;
181         memcpy(write_loc + stamp_size, ev.buffer, ev.size);
182
183         _size += stamp_size + ev.size;
184         _silent = false;
185         
186         return true;
187 }
188
189
190 /** Reserve space for a new event in the buffer.
191  *
192  * This call is for copying MIDI directly into the buffer, the data location
193  * (of sufficient size to write \a size bytes) is returned, or 0 on failure.
194  * This call MUST be immediately followed by a write to the returned data
195  * location, or the buffer will be corrupted and very nasty things will happen.
196  */
197 uint8_t*
198 MidiBuffer::reserve(TimeType time, size_t size)
199 {
200         const size_t stamp_size = sizeof(TimeType);
201         if (_size + stamp_size + size >= _capacity) {
202                 return 0;
203         }
204
205         // write timestamp
206         uint8_t* write_loc = _data + _size;
207         *((TimeType*)write_loc) = time;
208         
209         // move write_loc to begin of MIDI buffer data to write to
210         write_loc += stamp_size;
211
212         _size += stamp_size + size;
213         _silent = false;
214         
215         return write_loc;
216 }
217
218
219 void
220 MidiBuffer::silence(nframes_t dur, nframes_t offset)
221 {
222         // FIXME use parameters
223         if (offset != 0) {
224                 cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl;
225         }
226
227         _size = 0;
228         _silent = true;
229 }
230
231 /** Merge \a other into this buffer.  Realtime safe. */
232 bool
233 MidiBuffer::merge_in_place(const MidiBuffer &other)
234 {
235         if (other.size() == 0) {
236                 return true;
237         }
238
239         if (_size == 0) {
240                 copy(other);
241                 return true;
242         }
243
244         if (_size + other.size() > _capacity) {
245                 cerr << "MidiBuffer::merge failed (no space)" << endl;
246                 return false;
247         }
248         
249         cerr << "FIXME: MIDI BUFFER IN-PLACE MERGE" << endl;
250         return true;
251 }
252
253 /** Clear, and merge \a a and \a b into this buffer.
254  *
255  * \return true if complete merge was successful
256  */
257 bool
258 MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
259 {
260         _size = 0;
261         
262         if (this == &a) {
263             merge_in_place(b);
264         }
265
266         if (this == &b) {
267             merge_in_place(a);
268         }
269         
270         cerr << "FIXME: MIDI BUFFER MERGE" << endl;
271         return true;
272 }
273