Progress on the disk side of things:
[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 #if 0
128 inline size_t
129 MidiRingBuffer::read (MidiBuffer& buf)
130 {
131         const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr);
132
133         if (read_space() == 0) {
134                 return 0;
135         } else {
136                 MidiEvent* const read_ev = &_ev_buf[priv_read_ptr];
137                 assert(read_ev->size > 0);
138                 buf.push_back(*read_ev);
139                 printf("MRB - read %xd %d %d with time %u at index %zu\n",
140                         read_ev->buffer[0], read_ev->buffer[1], read_ev->buffer[2], read_ev->time,
141                         priv_read_ptr);
142                 clear_event(priv_read_ptr);
143                 g_atomic_int_set(&_read_ptr, (priv_read_ptr + 1) % _size);
144                 return 1;
145         }
146 }
147 #endif
148 inline size_t
149 MidiRingBuffer::write (const MidiEvent& ev)
150 {
151         //static jack_nframes_t last_write_time = 0;
152         
153         assert(ev.size > 0);
154
155         size_t priv_write_ptr = g_atomic_int_get(&_write_ptr);
156
157         if (write_space () == 0) {
158                 return 0;
159         } else {
160                 //assert(ev.time >= last_write_time);
161
162                 const size_t raw_index = priv_write_ptr * _max_event_size;
163
164                 MidiEvent* const write_ev = &_ev_buf[priv_write_ptr];
165                 *write_ev = ev;
166
167                 memcpy(&_raw_buf[raw_index], ev.buffer, ev.size);
168                 write_ev->buffer = &_raw_buf[raw_index];
169         g_atomic_int_set(&_write_ptr, (priv_write_ptr + 1) % _size);
170                 
171                 printf("MRB - wrote %xd %d %d with time %u at index %zu (raw index %zu)\n",
172                         write_ev->buffer[0], write_ev->buffer[1], write_ev->buffer[2], write_ev->time,
173                         priv_write_ptr, raw_index);
174                 
175                 assert(write_ev->size = ev.size);
176
177                 //last_write_time = ev.time;
178                 printf("(W) read space: %zu\n", read_space());
179
180                 return 1;
181         }
182 }
183
184 inline size_t
185 MidiRingBuffer::read(MidiBuffer& dst, jack_nframes_t start, jack_nframes_t end)
186 {
187         if (read_space() == 0)
188                 return 0;
189
190         size_t         priv_read_ptr = g_atomic_int_get(&_read_ptr);
191         jack_nframes_t time          = _ev_buf[priv_read_ptr].time;
192         size_t         count         = 0;
193         size_t         limit         = read_space();
194
195         while (time <= end && limit > 0) {
196                 MidiEvent* const read_ev = &_ev_buf[priv_read_ptr];
197                 if (time >= start) {
198                         dst.push_back(*read_ev);
199                         printf("MRB - read %xd %d %d with time %u at index %zu\n",
200                                 read_ev->buffer[0], read_ev->buffer[1], read_ev->buffer[2], read_ev->time,
201                                 priv_read_ptr);
202                 } else {
203                         cerr << "MRB: LOST EVENT!" << endl;
204                 }
205
206                 clear_event(priv_read_ptr);
207
208                 ++count;
209                 --limit;
210                 
211                 priv_read_ptr = (priv_read_ptr + 1) % _size;
212                 
213                 assert(read_ev->time <= end);
214                 time = _ev_buf[priv_read_ptr].time;
215         }
216         
217         g_atomic_int_set(&_read_ptr, priv_read_ptr);
218         printf("(R) read space: %zu\n", read_space());
219
220         return count;
221 }
222
223 inline size_t
224 MidiRingBuffer::write(const MidiBuffer& in, jack_nframes_t offset)
225 {
226         size_t num_events = in.size();
227         size_t to_write = std::min(write_space(), num_events);
228
229         // FIXME: double copy :/
230         for (size_t i=0; i < to_write; ++i) {
231                 MidiEvent ev = in[i];
232                 ev.time += offset;
233                 write(ev);
234         }
235
236         return to_write;
237 }
238
239 } // namespace ARDOUR
240
241 #endif // __ardour_midi_ring_buffer_h__
242