2 Copyright (C) 1998 Paul Barton-Davis
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.
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.
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.
23 #include "pbd/error.h"
24 #include "pbd/stacktrace.h"
26 #include "midi++/types.h"
28 #include "ardour/async_midi_port.h"
29 #include "ardour/audioengine.h"
30 #include "ardour/midi_buffer.h"
33 using namespace ARDOUR;
38 template class EventRingBuffer<timestamp_t>;
41 pthread_t AsyncMIDIPort::_process_thread;
43 #define port_engine AudioEngine::instance()->port_engine()
45 AsyncMIDIPort::AsyncMIDIPort (string const & name, PortFlags flags)
46 : MidiPort (name, flags)
47 , MIDI::Port (name, MIDI::Port::Flags (0))
48 , _currently_in_cycle (false)
49 , _last_write_timestamp (0)
56 AsyncMIDIPort::~AsyncMIDIPort ()
61 AsyncMIDIPort::flush_output_fifo (pframes_t nframes)
63 RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0 } };
66 output_fifo.get_read_vector (&vec);
68 MidiBuffer& mb (get_midi_buffer (nframes));
71 Evoral::Event<double>* evp = vec.buf[0];
73 for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
74 mb.push_back (evp->time(), evp->size(), evp->buffer());
79 Evoral::Event<double>* evp = vec.buf[1];
81 for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
82 mb.push_back (evp->time(), evp->size(), evp->buffer());
86 if ((written = vec.len[0] + vec.len[1]) != 0) {
87 output_fifo.increment_read_idx (written);
92 AsyncMIDIPort::cycle_start (pframes_t nframes)
94 _currently_in_cycle = true;
95 MidiPort::cycle_start (nframes);
97 /* dump anything waiting in the output FIFO at the start of the port
101 if (ARDOUR::Port::sends_output()) {
102 flush_output_fifo (nframes);
105 /* copy incoming data from the port buffer into the input FIFO
106 and if necessary wakeup the reader
109 if (ARDOUR::Port::receives_input()) {
110 MidiBuffer& mb (get_midi_buffer (nframes));
111 pframes_t when = AudioEngine::instance()->sample_time_at_cycle_start();
113 for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) {
114 input_fifo.write (when, (Evoral::EventType) 0, (*b).size(), (*b).buffer());
125 AsyncMIDIPort::cycle_end (pframes_t nframes)
127 if (ARDOUR::Port::sends_output()) {
128 /* move any additional data from output FIFO into the port
131 flush_output_fifo (nframes);
134 MidiPort::cycle_end (nframes);
136 _currently_in_cycle = false;
139 /** wait for the output FIFO to be emptied by successive process() callbacks.
141 * Cannot be called from a processing thread.
144 AsyncMIDIPort::drain (int check_interval_usecs)
146 RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
148 if (is_process_thread()) {
149 error << "Process thread called MIDI::AsyncMIDIPort::drain() - this cannot work" << endmsg;
154 output_fifo.get_write_vector (&vec);
155 if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) {
158 usleep (check_interval_usecs);
163 AsyncMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp)
167 if (!ARDOUR::Port::sends_output()) {
171 if (!is_process_thread()) {
173 /* this is the best estimate of "when" this MIDI data is being
177 _parser->set_timestamp (AudioEngine::instance()->sample_time() + timestamp);
178 for (size_t n = 0; n < msglen; ++n) {
179 _parser->scanner (msg[n]);
182 Glib::Threads::Mutex::Lock lm (output_fifo_lock);
183 RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
185 output_fifo.get_write_vector (&vec);
187 if (vec.len[0] + vec.len[1] < 1) {
188 error << "no space in FIFO for non-process thread MIDI write" << endmsg;
193 if (!vec.buf[0]->owns_buffer()) {
194 vec.buf[0]->set_buffer (0, 0, true);
196 vec.buf[0]->set (msg, msglen, timestamp);
198 if (!vec.buf[1]->owns_buffer()) {
199 vec.buf[1]->set_buffer (0, 0, true);
201 vec.buf[1]->set (msg, msglen, timestamp);
204 output_fifo.increment_write_idx (1);
210 _parser->set_timestamp (AudioEngine::instance()->sample_time_at_cycle_start() + timestamp);
211 for (size_t n = 0; n < msglen; ++n) {
212 _parser->scanner (msg[n]);
215 if (timestamp >= _cycle_nframes) {
216 std::cerr << "attempting to write MIDI event of " << msglen << " bytes at time "
217 << timestamp << " of " << _cycle_nframes
218 << " (this will not work - needs a code fix)"
222 /* This is the process thread, which makes checking
223 * _currently_in_cycle atomic and safe, since it is only
224 * set from cycle_start() and cycle_end(), also called
225 * only from the process thread.
228 if (_currently_in_cycle) {
230 MidiBuffer& mb (get_midi_buffer (_cycle_nframes));
232 if (timestamp == 0) {
233 timestamp = _last_write_timestamp;
236 if (mb.push_back (timestamp, msglen, msg)) {
238 _last_write_timestamp = timestamp;
241 cerr << "AsyncMIDIPort (" << ARDOUR::Port::name() << "): write of " << msglen << " @ " << timestamp << " failed\n" << endl;
242 PBD::stacktrace (cerr, 20);
246 cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
247 PBD::stacktrace (cerr, 20);
256 AsyncMIDIPort::read (MIDI::byte *, size_t)
258 if (!ARDOUR::Port::receives_input()) {
263 Evoral::EventType type;
265 byte buffer[input_fifo.capacity()];
267 while (input_fifo.read (&time, &type, &size, buffer)) {
268 _parser->set_timestamp (time);
269 for (uint32_t i = 0; i < size; ++i) {
270 _parser->scanner (buffer[i]);
278 AsyncMIDIPort::parse (framecnt_t)
282 /* see ::read() to realize why buf is not used */
283 read (buf, sizeof (buf));
287 AsyncMIDIPort::set_process_thread (pthread_t thr)
289 _process_thread = thr;
293 AsyncMIDIPort::is_process_thread()
295 return pthread_equal (pthread_self(), _process_thread);