Merged with trunk R846
[ardour.git] / libs / ardour / ardour / midi_ring_buffer.h
1 /*
2     Copyright (C) 2006 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 #ifndef __ardour_midi_ring_buffer_h__
20 #define __ardour_midi_ring_buffer_h__
21
22 #include <algorithm>
23 #include <ardour/types.h>
24 #include <pbd/ringbufferNPT.h>
25 #include <ardour/buffer.h>
26
27 namespace ARDOUR {
28
29 /** A MIDI RingBuffer
30  * (necessary because MIDI events are variable sized so a generic RB won't do).
31  *
32  * ALL publically accessible sizes refer to event COUNTS.  What actually goes
33  * on in here is none of the callers business :)
34  */
35 class MidiRingBuffer {
36 public:
37         MidiRingBuffer (size_t size)
38                 : _size(size)
39                 , _max_event_size(MidiBuffer::max_event_size())
40                 , _ev_buf(new MidiEvent[size])
41                 , _raw_buf(new RawMidi[size * _max_event_size])
42         {
43                 reset ();
44                 assert(read_space() == 0);
45                 assert(write_space() == size - 1);
46         }
47         
48         virtual ~MidiRingBuffer() {
49                 delete[] _ev_buf;
50                 delete[] _raw_buf;
51         }
52
53         void reset () {
54                 /* !!! NOT THREAD SAFE !!! */
55                 g_atomic_int_set (&_write_ptr, 0);
56                 g_atomic_int_set (&_read_ptr, 0);
57         }
58
59         size_t write_space () {
60                 size_t w, r;
61                 
62                 w = g_atomic_int_get (&_write_ptr);
63                 r = g_atomic_int_get (&_read_ptr);
64                 
65                 if (w > r) {
66                         return ((r - w + _size) % _size) - 1;
67                 } else if (w < r) {
68                         return (r - w) - 1;
69                 } else {
70                         return _size - 1;
71                 }
72         }
73         
74         size_t read_space () {
75                 size_t w, r;
76                 
77                 w = g_atomic_int_get (&_write_ptr);
78                 r = g_atomic_int_get (&_read_ptr);
79                 
80                 if (w > r) {
81                         return w - r;
82                 } else {
83                         return (w - r + _size) % _size;
84                 }
85         }
86
87         size_t capacity() const { return _size; }
88
89         /** Read one event and appends it to @a out. */
90         //size_t read(MidiBuffer& out);
91
92         /** Write one event (@a in) */
93         size_t write(const MidiEvent& in); // deep copies in
94
95         /** Read events all events up to time @a end into @a out, leaving stamps intact.
96          * Any events before @a start will be dropped. */
97         size_t read(MidiBuffer& out, jack_nframes_t start, jack_nframes_t end);
98
99         /** Write all events from @a in, applying @a offset to all time stamps */
100         size_t write(const MidiBuffer& in, jack_nframes_t offset = 0);
101
102         inline void clear_event(size_t index);
103
104 private:
105
106         // _event_ indices
107         mutable gint _write_ptr;
108         mutable gint _read_ptr;
109         
110         size_t     _size;           // size (capacity) in events
111         size_t     _max_event_size; // ratio of raw_buf size to ev_buf size
112         MidiEvent* _ev_buf;         // these point into...
113         RawMidi*   _raw_buf;        // this
114
115 };
116
117 /** Just for sanity checking */
118 inline void
119 MidiRingBuffer::clear_event(size_t index)
120 {
121         memset(&_ev_buf[index].buffer, 0, _max_event_size);
122         _ev_buf[index].time = 0;
123         _ev_buf[index].size = 0;
124         _ev_buf[index].buffer = 0;
125
126 }
127
128 inline size_t
129 MidiRingBuffer::write (const MidiEvent& ev)
130 {
131         //static jack_nframes_t last_write_time = 0;
132         
133         assert(ev.size > 0);
134
135         size_t priv_write_ptr = g_atomic_int_get(&_write_ptr);
136
137         if (write_space () == 0) {
138                 return 0;
139         } else {
140                 //assert(ev.time >= last_write_time);
141
142                 const size_t raw_index = priv_write_ptr * _max_event_size;
143
144                 MidiEvent* const write_ev = &_ev_buf[priv_write_ptr];
145                 *write_ev = ev;
146
147                 memcpy(&_raw_buf[raw_index], ev.buffer, ev.size);
148                 write_ev->buffer = &_raw_buf[raw_index];
149         g_atomic_int_set(&_write_ptr, (priv_write_ptr + 1) % _size);
150                 
151                 //printf("MRB - wrote %xd %d %d with time %u at index %zu (raw index %zu)\n",
152                 //      write_ev->buffer[0], write_ev->buffer[1], write_ev->buffer[2], write_ev->time,
153                 //      priv_write_ptr, raw_index);
154                 
155                 assert(write_ev->size = ev.size);
156
157                 //last_write_time = ev.time;
158                 //printf("(W) read space: %zu\n", read_space());
159
160                 return 1;
161         }
162 }
163
164 inline size_t
165 MidiRingBuffer::read(MidiBuffer& dst, jack_nframes_t start, jack_nframes_t end)
166 {
167         if (read_space() == 0)
168                 return 0;
169
170         size_t         priv_read_ptr = g_atomic_int_get(&_read_ptr);
171         jack_nframes_t time          = _ev_buf[priv_read_ptr].time;
172         size_t         count         = 0;
173         size_t         limit         = read_space();
174
175         while (time <= end && limit > 0) {
176                 MidiEvent* const read_ev = &_ev_buf[priv_read_ptr];
177                 if (time >= start) {
178                         dst.push_back(*read_ev);
179                         //printf("MRB - read %#X %d %d with time %u at index %zu\n",
180                         //      read_ev->buffer[0], read_ev->buffer[1], read_ev->buffer[2], read_ev->time,
181                         //      priv_read_ptr);
182                 } else {
183                         printf("MRB - SKIPPING - %#X %d %d with time %u at index %zu\n",
184                                 read_ev->buffer[0], read_ev->buffer[1], read_ev->buffer[2], read_ev->time,
185                                 priv_read_ptr);
186                         break;
187                 }
188
189                 clear_event(priv_read_ptr);
190
191                 ++count;
192                 --limit;
193                 
194                 priv_read_ptr = (priv_read_ptr + 1) % _size;
195                 
196                 assert(read_ev->time <= end);
197                 time = _ev_buf[priv_read_ptr].time;
198         }
199         
200         g_atomic_int_set(&_read_ptr, priv_read_ptr);
201         
202         //printf("(R) read space: %zu\n", read_space());
203
204         return count;
205 }
206
207 inline size_t
208 MidiRingBuffer::write(const MidiBuffer& in, jack_nframes_t offset)
209 {
210         size_t num_events = in.size();
211         size_t to_write = std::min(write_space(), num_events);
212
213         // FIXME: double copy :/
214         for (size_t i=0; i < to_write; ++i) {
215                 MidiEvent ev = in[i];
216                 ev.time += offset;
217                 write(ev);
218         }
219
220         return to_write;
221 }
222
223 } // namespace ARDOUR
224
225 #endif // __ardour_midi_ring_buffer_h__
226