b0e46f8ad1a8cb03b625c52b532bb35d56ae8715
[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)" 
139                      << endl;
140                 return false;
141         }
142
143         uint8_t* const write_loc = _data + _size;
144         *((TimeType*)write_loc) = ev.time();
145         memcpy(write_loc + stamp_size, ev.buffer(), ev.size());
146
147         _size += stamp_size + ev.size();
148         _silent = false;
149         
150         return true;
151 }
152
153
154 /** Push an event into the buffer.
155  *
156  * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
157  * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
158  * Realtime safe.
159  * @return false if operation failed (not enough room)
160  */
161 bool
162 MidiBuffer::push_back(const jack_midi_event_t& ev)
163 {
164         const size_t stamp_size = sizeof(TimeType);
165         if (_size + stamp_size + ev.size >= _capacity) {
166                 cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
167                 return false;
168         }
169
170         uint8_t* const write_loc = _data + _size;
171         *((TimeType*)write_loc) = ev.time;
172         memcpy(write_loc + stamp_size, ev.buffer, ev.size);
173
174         _size += stamp_size + ev.size;
175         _silent = false;
176         
177         return true;
178 }
179
180
181 /** Reserve space for a new event in the buffer.
182  *
183  * This call is for copying MIDI directly into the buffer, the data location
184  * (of sufficient size to write \a size bytes) is returned, or 0 on failure.
185  * This call MUST be immediately followed by a write to the returned data
186  * location, or the buffer will be corrupted and very nasty things will happen.
187  */
188 uint8_t*
189 MidiBuffer::reserve(TimeType time, size_t size)
190 {
191         const size_t stamp_size = sizeof(TimeType);
192         if (_size + stamp_size + size >= _capacity) {
193                 return 0;
194         }
195
196         // write timestamp
197         uint8_t* write_loc = _data + _size;
198         *((TimeType*)write_loc) = time;
199         
200         // move write_loc to begin of MIDI buffer data to write to
201         write_loc += stamp_size;
202
203         _size += stamp_size + size;
204         _silent = false;
205         
206         return write_loc;
207 }
208
209
210 void
211 MidiBuffer::silence(nframes_t dur, nframes_t offset)
212 {
213         // FIXME use parameters
214         if (offset != 0) {
215                 cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl;
216         }
217
218         _size = 0;
219         _silent = true;
220 }
221
222 /** Merge \a other into this buffer.  Realtime safe. */
223 bool
224 MidiBuffer::merge_in_place(const MidiBuffer &other)
225 {
226         if (other.size() == 0) {
227                 return true;
228         }
229
230         if (_size == 0) {
231                 copy(other);
232                 return true;
233         }
234
235         if (_size + other.size() > _capacity) {
236                 cerr << "MidiBuffer::merge failed (no space)" << endl;
237                 return false;
238         }
239         
240         cerr << "FIXME: MIDI BUFFER IN-PLACE MERGE" << endl;
241         return true;
242 }
243
244 /** Clear, and merge \a a and \a b into this buffer.
245  *
246  * \return true if complete merge was successful
247  */
248 bool
249 MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
250 {
251         _size = 0;
252         
253         if (this == &a) {
254             merge_in_place(b);
255         }
256
257         if (this == &b) {
258             merge_in_place(a);
259         }
260         
261         cerr << "FIXME: MIDI BUFFER MERGE" << endl;
262         return true;
263 }
264