Merge branch 'cairocanvas' of git.ardour.org:ardour/ardour into cairocanvas
[ardour.git] / libs / ardour / async_midi_port.cc
1 /*
2     Copyright (C) 1998 Paul Barton-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     $Id$
19 */
20
21 #include <iostream>
22 #include <vector>
23
24 #include <glibmm/timer.h>
25
26 #include "pbd/error.h"
27 #include "pbd/stacktrace.h"
28
29 #include "midi++/types.h"
30
31 #include "ardour/async_midi_port.h"
32 #include "ardour/audioengine.h"
33 #include "ardour/midi_buffer.h"
34
35 using namespace MIDI;
36 using namespace ARDOUR;
37 using namespace std;
38 using namespace PBD;
39
40 namespace Evoral {
41         template class EventRingBuffer<MIDI::timestamp_t>;
42 }
43
44 pthread_t AsyncMIDIPort::_process_thread;
45
46 #define port_engine AudioEngine::instance()->port_engine()
47
48 AsyncMIDIPort::AsyncMIDIPort (string const & name, PortFlags flags)
49         : MidiPort (name, flags)
50         , MIDI::Port (name, MIDI::Port::Flags (0))
51         , _currently_in_cycle (false)
52         , _last_write_timestamp (0)
53         , have_timer (false)
54         , output_fifo (512)
55         , input_fifo (1024)
56 #ifndef PLATFORM_WINDOWS
57         , xthread (true)
58 #endif
59 {
60 }
61
62 AsyncMIDIPort::~AsyncMIDIPort ()
63 {
64 }
65
66 void
67 AsyncMIDIPort::set_timer (boost::function<framecnt_t (void)>& f)
68 {
69         timer = f;
70         have_timer = true;
71 }
72
73 void
74 AsyncMIDIPort::flush_output_fifo (MIDI::pframes_t nframes)
75 {
76         RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0 } };
77         size_t written;
78
79         output_fifo.get_read_vector (&vec);
80
81         MidiBuffer& mb (get_midi_buffer (nframes));
82                         
83         if (vec.len[0]) {
84                 Evoral::Event<double>* evp = vec.buf[0];
85                 
86                 for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
87                         mb.push_back (evp->time(), evp->size(), evp->buffer());
88                 }
89         }
90         
91         if (vec.len[1]) {
92                 Evoral::Event<double>* evp = vec.buf[1];
93
94                 for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
95                         mb.push_back (evp->time(), evp->size(), evp->buffer());
96                 }
97         }
98         
99         if ((written = vec.len[0] + vec.len[1]) != 0) {
100                 output_fifo.increment_read_idx (written);
101         }
102 }
103
104 void
105 AsyncMIDIPort::cycle_start (MIDI::pframes_t nframes)
106 {
107         _currently_in_cycle = true;
108         MidiPort::cycle_start (nframes);
109
110         /* dump anything waiting in the output FIFO at the start of the port
111          * buffer
112          */
113
114         if (ARDOUR::Port::sends_output()) {
115                 flush_output_fifo (nframes);
116         } 
117         
118         /* copy incoming data from the port buffer into the input FIFO
119            and if necessary wakeup the reader
120         */
121
122         if (ARDOUR::Port::receives_input()) {
123                 MidiBuffer& mb (get_midi_buffer (nframes));
124                 framecnt_t when;
125                 
126                 if (have_timer) {
127                         when = timer ();
128                 } else {
129                         when = AudioEngine::instance()->sample_time_at_cycle_start();
130                 }
131
132                 for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) {
133                         if (!have_timer) {
134                                 when += (*b).time();
135                         }
136                         input_fifo.write (when, (Evoral::EventType) 0, (*b).size(), (*b).buffer());
137                 }
138
139 #ifndef PLATFORM_WINDOWS
140                 if (!mb.empty()) {
141                         xthread.wakeup ();
142                 }
143 #endif
144         }
145 }
146
147 void
148 AsyncMIDIPort::cycle_end (MIDI::pframes_t nframes)
149 {
150         if (ARDOUR::Port::sends_output()) {
151                 /* move any additional data from output FIFO into the port
152                    buffer.
153                 */
154                 flush_output_fifo (nframes);
155         }
156
157         MidiPort::cycle_end (nframes);
158
159         _currently_in_cycle = false;
160 }
161
162 /** wait for the output FIFO to be emptied by successive process() callbacks.
163  *
164  * Cannot be called from a processing thread.
165  */
166 void
167 AsyncMIDIPort::drain (int check_interval_usecs)
168 {
169         RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
170
171         if (!AudioEngine::instance()->running() || AudioEngine::instance()->session() == 0) {
172                 /* no more process calls - it will never drain */
173                 return;
174         }
175
176
177         if (is_process_thread()) {
178                 error << "Process thread called MIDI::AsyncMIDIPort::drain() - this cannot work" << endmsg;
179                 return;
180         }
181
182         while (1) {
183                 output_fifo.get_write_vector (&vec);
184                 if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) {
185                         break;
186                 }
187                 Glib::usleep (check_interval_usecs);
188         }
189 }
190
191 int
192 AsyncMIDIPort::write (const MIDI::byte * msg, size_t msglen, MIDI::timestamp_t timestamp)
193 {
194         int ret = 0;
195
196         if (!ARDOUR::Port::sends_output()) {
197                 return ret;
198         }
199
200         if (!is_process_thread()) {
201
202                 /* this is the best estimate of "when" this MIDI data is being
203                  * delivered
204                  */
205                 
206                 _parser->set_timestamp (AudioEngine::instance()->sample_time() + timestamp);
207                 for (size_t n = 0; n < msglen; ++n) {
208                         _parser->scanner (msg[n]);
209                 }
210
211                 Glib::Threads::Mutex::Lock lm (output_fifo_lock);
212                 RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
213                 
214                 output_fifo.get_write_vector (&vec);
215
216                 if (vec.len[0] + vec.len[1] < 1) {
217                         error << "no space in FIFO for non-process thread MIDI write" << endmsg;
218                         return 0;
219                 }
220
221                 if (vec.len[0]) {
222                         if (!vec.buf[0]->owns_buffer()) {
223                                 vec.buf[0]->set_buffer (0, 0, true);
224                         }
225                         vec.buf[0]->set (msg, msglen, timestamp);
226                 } else {
227                         if (!vec.buf[1]->owns_buffer()) {
228                                 vec.buf[1]->set_buffer (0, 0, true);
229                         }
230                         vec.buf[1]->set (msg, msglen, timestamp);
231                 }
232
233                 output_fifo.increment_write_idx (1);
234                 
235                 ret = msglen;
236
237         } else {
238
239                 _parser->set_timestamp (AudioEngine::instance()->sample_time_at_cycle_start() + timestamp);
240                 for (size_t n = 0; n < msglen; ++n) {
241                         _parser->scanner (msg[n]);
242                 }
243
244                 if (timestamp >= _cycle_nframes) {
245                         std::cerr << "attempting to write MIDI event of " << msglen << " MIDI::bytes at time "
246                                   << timestamp << " of " << _cycle_nframes
247                                   << " (this will not work - needs a code fix)"
248                                   << std::endl;
249                 }
250
251                 /* This is the process thread, which makes checking
252                  * _currently_in_cycle atomic and safe, since it is only
253                  * set from cycle_start() and cycle_end(), also called
254                  * only from the process thread.
255                  */
256
257                 if (_currently_in_cycle) {
258
259                         MidiBuffer& mb (get_midi_buffer (_cycle_nframes));
260                         
261                         if (timestamp == 0) {
262                                 timestamp = _last_write_timestamp;
263                         } 
264                         
265                         if (mb.push_back (timestamp, msglen, msg)) {
266                                 ret = msglen;
267                                 _last_write_timestamp = timestamp;
268
269                         } else {
270                                 cerr << "AsyncMIDIPort (" << ARDOUR::Port::name() << "): write of " << msglen << " @ " << timestamp << " failed\n" << endl;
271                                 PBD::stacktrace (cerr, 20);
272                                 ret = 0;
273                         }
274                 } else {
275                         cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
276                         PBD::stacktrace (cerr, 20);
277                 }
278         }
279
280         return ret;
281 }
282
283
284 int
285 AsyncMIDIPort::read (MIDI::byte *, size_t)
286 {
287         if (!ARDOUR::Port::receives_input()) {
288                 return 0;
289         }
290         
291         timestamp_t time;
292         Evoral::EventType type;
293         uint32_t size;
294         vector<MIDI::byte> buffer(input_fifo.capacity());
295
296         while (input_fifo.read (&time, &type, &size, &buffer[0])) {
297                 _parser->set_timestamp (time);
298                 for (uint32_t i = 0; i < size; ++i) {
299                         _parser->scanner (buffer[i]);
300                 }
301         }
302
303         return 0;
304 }
305
306 void
307 AsyncMIDIPort::parse (MIDI::framecnt_t)
308 {
309         MIDI::byte buf[1];
310
311         /* see ::read() to realize why buf is not used */
312         read (buf, sizeof (buf));
313 }
314
315 void
316 AsyncMIDIPort::set_process_thread (pthread_t thr)
317 {
318         _process_thread = thr;
319 }
320
321 bool
322 AsyncMIDIPort::is_process_thread()
323 {
324         return pthread_equal (pthread_self(), _process_thread);
325 }
326