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