change MidiPlaylist::dump() into ::render(); change type of initial argument
[ardour.git] / libs / ardour / rt_midibuffer.cc
1 /*
2  * Copyright (C) 2019 Paul Davis <paul@linuxaudiosystems.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <iostream>
20
21 #include "pbd/malign.h"
22 #include "pbd/compose.h"
23 #include "pbd/error.h"
24 #include "pbd/debug.h"
25 #include "pbd/stacktrace.h"
26
27 #include "evoral/midi_util.h"
28
29 #include "ardour/debug.h"
30 #include "ardour/midi_buffer.h"
31 #include "ardour/midi_state_tracker.h"
32 #include "ardour/rt_midibuffer.h"
33
34 using namespace std;
35 using namespace ARDOUR;
36 using namespace PBD;
37
38 RTMidiBuffer::RTMidiBuffer (size_t capacity)
39         : _size (0)
40         , _capacity (0)
41         , _data (0)
42         , _pool_size (0)
43         , _pool_capacity (0)
44         , _pool (0)
45 {
46         if (capacity) {
47                 resize (capacity);
48         }
49 }
50
51 RTMidiBuffer::~RTMidiBuffer()
52 {
53         cache_aligned_free (_data);
54         cache_aligned_free (_pool);
55 }
56
57 void
58 RTMidiBuffer::resize (size_t size)
59 {
60         if (_data && size < _capacity) {
61
62                 if (_size < size) {
63                         /* truncate */
64                         _size = size;
65                 }
66
67                 return;
68         }
69
70         Item* old_data = _data;
71
72         cache_aligned_malloc ((void**) &_data, size * sizeof (Item));
73
74         if (_size) {
75                 memcpy (_data, old_data, _size);
76                 cache_aligned_free (old_data);
77         }
78
79         _capacity = size;
80 }
81
82 void
83 RTMidiBuffer::dump (uint32_t cnt)
84 {
85         cerr << this << " total items: " << _size << " within " << _capacity << " blob pool: " << _pool_capacity << " used " << _pool_size << endl;
86
87         for (uint32_t i = 0; i < _size && i < cnt; ++i) {
88
89                 Item* item = &_data[i];
90                 uint8_t* addr;
91                 uint32_t size;
92
93                 if (item->bytes[0]) {
94
95                         /* more than 3 bytes ... indirect */
96
97                         uint32_t offset = item->offset & ~(1<<(sizeof(uint8_t)-1));
98                         Blob* blob = reinterpret_cast<Blob*> (&_pool[offset]);
99
100                         size = blob->size;
101                         addr = blob->data;
102
103                 } else {
104
105                         size = Evoral::midi_event_size (item->bytes[1]);
106                         addr = &item->bytes[1];
107
108                 }
109
110                 cerr << "@ " << item->timestamp << " sz=" << size << '\t';
111
112                 cerr << hex;
113                 for (size_t j =0 ; j < size; ++j) {
114                         cerr << "0x" << hex << (int)addr[j] << dec << '/' << (int)addr[i] << ' ';
115                 }
116                 cerr << dec << endl;
117         }
118 }
119
120 uint32_t
121 RTMidiBuffer::write (TimeType time, Evoral::EventType /*type*/, uint32_t size, const uint8_t* buf)
122 {
123         /* This buffer stores only MIDI, we don't care about the value of "type" */
124
125         if (_size == _capacity) {
126                 resize (_capacity + 1024); // XXX 1024 is completely arbitrary
127         }
128
129         _data[_size].timestamp = time;
130
131         if (size > 3) {
132
133                 uint32_t off = store_blob (size, buf);
134
135                 /* this indicates that the data (more than 3 bytes) is not inline */
136                 _data[_size].offset = (off | (1<<(sizeof(uint8_t)-1)));
137
138         } else {
139
140                 assert ((int) size == Evoral::midi_event_size (buf[0]));
141
142                 /* this indicates that the data (up to 3 bytes) is inline */
143                 _data[_size].bytes[0] = 0;
144
145                 switch (size) {
146                 case 3:
147                         _data[_size].bytes[3] = buf[2];
148                         /* fallthru */
149                 case 2:
150                         _data[_size].bytes[2] = buf[1];
151                         /* fallthru */
152                 case 1:
153                         _data[_size].bytes[1] = buf[0];
154                         break;
155                 }
156         }
157
158         ++_size;
159
160         return size;
161 }
162
163 static
164 bool
165 item_timestamp_earlier (ARDOUR::RTMidiBuffer::Item const & item, samplepos_t time)
166 {
167         return item.timestamp < time;
168 }
169
170
171
172 uint32_t
173 RTMidiBuffer::read (MidiBuffer& dst, samplepos_t start, samplepos_t end, MidiStateTracker& tracker, samplecnt_t offset)
174 {
175         Item* iend = _data+_size;
176         Item* item = lower_bound (_data, iend, start, item_timestamp_earlier);
177         uint32_t count = 0;
178
179 #ifndef NDEBUG
180         TimeType unadjusted_time;
181 #endif
182
183         DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("read from %1 .. %2 .. initial index = %3 (time = %4)\n", start, end, item, item->timestamp));
184
185         while ((item < iend) && (item->timestamp < end)) {
186
187                 TimeType evtime = item->timestamp;
188
189 #ifndef NDEBUG
190                 unadjusted_time = evtime;
191 #endif
192                 /* Adjust event times to be relative to 'start', taking
193                  * 'offset' into account.
194                  */
195
196                 evtime -= start;
197                 evtime += offset;
198
199                 uint32_t size;
200                 uint8_t* addr;
201
202                 if (item->bytes[0]) {
203
204                         /* more than 3 bytes ... indirect */
205
206                         uint32_t offset = item->offset & ~(1<<(sizeof(uint8_t)-1));
207                         Blob* blob = reinterpret_cast<Blob*> (&_pool[offset]);
208
209                         size = blob->size;
210                         addr = blob->data;
211
212                 } else {
213
214                         size = Evoral::midi_event_size (item->bytes[1]);
215                         addr = &item->bytes[1];
216
217                 }
218
219                 uint8_t* write_loc = dst.reserve (evtime, size);
220
221                 if (write_loc == 0) {
222                         DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("MidiRingBuffer: overflow in destination MIDI buffer, stopped after %1 events, dst size = %2\n", count, dst.size()));
223                         break;
224                 }
225
226                 memcpy (write_loc, addr, size);
227
228                 DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("read event sz %1 @ %2\n", size, unadjusted_time));
229                 tracker.track (addr);
230
231                 ++item;
232                 ++count;
233         }
234
235         DEBUG_TRACE (DEBUG::MidiRingBuffer, string_compose ("total events found for %1 .. %2 = %3\n", start, end, count));
236         return count;
237 }
238
239 uint32_t
240 RTMidiBuffer::alloc_blob (uint32_t size)
241 {
242         if (_pool_size + size > _pool_capacity) {
243                 uint8_t* old_pool = _pool;
244
245                 _pool_capacity += size * 4;
246
247                 cache_aligned_malloc ((void **) &_pool, _pool_capacity * 2);
248                 memcpy (_pool, old_pool, _pool_size);
249                 cache_aligned_free (old_pool);
250         }
251
252         uint32_t offset = _pool_size;
253         _pool_size += size;
254
255         return offset;
256 }
257
258 uint32_t
259 RTMidiBuffer::store_blob (uint32_t size, uint8_t const * data)
260 {
261         uint32_t offset = alloc_blob (size);
262         uint8_t* addr = &_pool[offset];
263
264         *(reinterpret_cast<uint32_t*> (addr)) = size;
265         addr += sizeof (size);
266         memcpy (addr, data, size);
267
268         return offset;
269 }
270
271 void
272 RTMidiBuffer::clear ()
273 {
274         /* mark main array as empty */
275         _size = 0;
276         /* free the entire current pool size, if any */
277         _pool_size = 0;
278 }