make per-region note-tracking in MidiPlaylist work correctly, or something very close...
[ardour.git] / libs / ardour / midi_ring_buffer.cc
1 /*
2     Copyright (C) 2006-2008 Paul Davis
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
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include "pbd/compose.h"
20
21 #include "ardour/debug.h"
22 #include "ardour/midi_ring_buffer.h"
23 #include "ardour/midi_buffer.h"
24 #include "ardour/event_type_map.h"
25
26 using namespace std;
27 using namespace ARDOUR;
28
29 /** Read a block of MIDI events from buffer into a MidiBuffer.
30  *
31  * Timestamps of events returned are relative to start (i.e. event with stamp 0
32  * occurred at start), with offset added.
33  */
34 template<typename T>
35 size_t
36 MidiRingBuffer<T>::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset)
37 {
38         if (this->read_space() == 0) {
39                 return 0;
40         }
41
42         T                 ev_time;
43         Evoral::EventType ev_type;
44         uint32_t          ev_size;
45
46         size_t count = 0;
47
48         while (this->read_space() >= sizeof(T) + sizeof(Evoral::EventType) + sizeof(uint32_t)) {
49
50                 this->full_peek(sizeof(T), (uint8_t*)&ev_time);
51
52                 if (ev_time > end) {
53                         DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 past end @ %2\n", ev_time, end));
54                         break;
55                 } else if (ev_time < start) {
56                         DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 before start @ %2\n", ev_time, start));
57                         break;
58                 } else {
59                         DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB event @ %1 in range %2 .. %3\n", ev_time, start, end));
60                 }
61
62                 bool success = read_prefix(&ev_time, &ev_type, &ev_size);
63                 if (!success) {
64                         cerr << "WARNING: error reading event prefix from MIDI ring" << endl;
65                         continue;
66                 }
67
68                 // This event marks a loop end (i.e. the next event's timestamp will be non-monotonic)
69                 if (ev_type == LoopEventType) {
70                         /*ev_time -= start;
71                           ev_time += offset;*/
72                         // cerr << "MRB loop boundary @ " << ev_time << endl;
73
74                         // Return without reading data or writing to buffer (loop events have no data)
75                         // FIXME: This is not correct, loses events after the loop this cycle
76                         return count + 1;
77                 }
78
79                 uint8_t status;
80                 success = this->full_peek(sizeof(uint8_t), &status);
81                 assert(success); // If this failed, buffer is corrupt, all hope is lost
82
83                 // Ignore event if it doesn't match channel filter
84                 if (is_channel_event(status) && get_channel_mode() == FilterChannels) {
85                         const uint8_t channel = status & 0x0F;
86                         if (!(get_channel_mask() & (1L << channel))) {
87                                 // cerr << "MRB skipping event due to channel mask" << endl;
88                                 this->skip(ev_size); // Advance read pointer to next event
89                                 continue;
90                         }
91                 }
92
93                 assert(ev_time >= start);
94                 ev_time -= start;
95                 ev_time += offset;
96
97                 // write the timestamp to address (write_loc - 1)
98                 uint8_t* write_loc = dst.reserve(ev_time, ev_size);
99                 if (write_loc == NULL) {
100                         // cerr << "MRB: Unable to reserve space in buffer, event skipped";
101                         continue;
102                 }
103
104                 // write MIDI buffer contents
105                 success = Evoral::EventRingBuffer<T>::full_read(ev_size, write_loc);
106
107 #ifndef NDEBUG
108                 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, "wrote MidiEvent to Buffer: ");
109                 for (size_t i=0; i < ev_size; ++i) {
110                         DEBUG_STR_SET(a, hex);
111                         DEBUG_STR(a) << "0x" << (int)write_loc[i] << ' ';
112                         DEBUG_TRACE (DEBUG::MidiDiskstreamIO, DEBUG_STR(a).str());
113                 }
114                 DEBUG_TRACE (DEBUG::MidiDiskstreamIO, "\n");
115 #endif
116
117                 if (success) {
118                         if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
119                                 write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F);
120                         }
121                         ++count;
122                 } else {
123                         cerr << "WARNING: error reading event contents from MIDI ring" << endl;
124                 }
125         }
126
127         return count;
128 }
129 template<typename T>
130 void
131 MidiRingBuffer<T>::dump(ostream& str)
132 {
133         size_t rspace;
134
135         if ((rspace = this->read_space()) == 0) {
136                 str << "MRB::dump: empty\n";
137                 return;
138         }
139
140         T                 ev_time;
141         Evoral::EventType ev_type;
142         uint32_t          ev_size;
143         size_t read_ptr = g_atomic_int_get (&this->_read_ptr);
144
145         str << "Dump @ " << read_ptr << endl;
146
147         while (1) {
148                 uint8_t* wp;
149                 uint8_t* data;
150                 size_t write_ptr;
151
152 #define space(r,w) ((w > r) ? (w - r) : ((w - r + this->_size) % this->_size))
153
154                 write_ptr  = g_atomic_int_get (&this->_write_ptr);
155                 if (space (read_ptr, write_ptr) < sizeof (T)) {
156                         break;
157                 }
158
159                 wp = &this->_buf[read_ptr];
160                 memcpy (&ev_time, wp, sizeof (T));
161                 read_ptr = (read_ptr + sizeof (T)) % this->_size;
162                 str << "time " << ev_time;
163
164                 write_ptr  = g_atomic_int_get (&this->_write_ptr);
165                 if (space (read_ptr, write_ptr) < sizeof (ev_type)) {
166                         break;
167                 }
168
169                 wp = &this->_buf[read_ptr];
170                 memcpy (&ev_type, wp, sizeof (ev_type));
171                 read_ptr = (read_ptr + sizeof (ev_type)) % this->_size;
172                 str << " type " << ev_type;
173
174                 write_ptr  = g_atomic_int_get (&this->_write_ptr);
175                 if (space (read_ptr, write_ptr) < sizeof (ev_size)) {
176                         str << "!OUT!\n";
177                         break;
178                 }
179
180                 wp = &this->_buf[read_ptr];
181                 memcpy (&ev_size, wp, sizeof (ev_size));
182                 read_ptr = (read_ptr + sizeof (ev_size)) % this->_size;
183                 str << " size " << ev_size;
184
185                 write_ptr  = g_atomic_int_get (&this->_write_ptr);
186                 if (space (read_ptr, write_ptr) < ev_size) {
187                         str << "!OUT!\n";
188                         break;
189                 }
190
191                 data = new uint8_t[ev_size];
192
193                 wp = &this->_buf[read_ptr];
194                 memcpy (data, wp, ev_size);
195                 read_ptr = (read_ptr + ev_size) % this->_size;
196
197                 for (uint32_t i = 0; i != ev_size; ++i) {
198                         str << ' ' << hex << (int) data[i] << dec;
199                 }
200
201                 str << endl;
202
203                 delete [] data;
204         }
205 }
206
207
208 template class MidiRingBuffer<nframes_t>;
209