make ALSA MIDI I/O work with timestamps; more MTC debug tracing
[ardour.git] / libs / midi++2 / jack_midiport.cc
1 /*
2   Copyright (C) 2006 Paul Davis 
3   Written by Dave Robillard
4  
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9  
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14  
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <fcntl.h>
21 #include <cerrno>
22 #include <cassert>
23 #include <cstdlib>
24
25 #include "pbd/error.h"
26
27 #include "midi++/types.h"
28 #include "midi++/jack.h"
29
30 using namespace std;
31 using namespace MIDI;
32 using namespace PBD;
33
34 pthread_t JACK_MidiPort::_process_thread;
35
36 JACK_MidiPort::JACK_MidiPort(const XMLNode& node, jack_client_t* jack_client)
37         : Port(node)
38         , _jack_client(jack_client)
39         , _jack_input_port(NULL)
40         , _jack_output_port(NULL)
41         , _last_read_index(0)
42         , non_process_thread_fifo (512)
43 {
44         int err = create_ports (node);
45
46         if (!err) {
47                 _ok = true;
48         }
49 }
50
51 JACK_MidiPort::~JACK_MidiPort()
52 {
53         if (_jack_input_port) {
54                 jack_port_unregister (_jack_client, _jack_input_port);
55                 _jack_input_port = 0;
56         }
57
58         if (_jack_output_port) {
59                 jack_port_unregister (_jack_client, _jack_input_port);
60                 _jack_input_port = 0;
61         }
62 }
63
64 void
65 JACK_MidiPort::cycle_start (nframes_t nframes)
66 {
67         Port::cycle_start(nframes);
68         assert(_nframes_this_cycle == nframes);
69         _last_read_index = 0;
70         _last_write_timestamp = 0;
71
72         if (_jack_output_port != 0) {
73                 // output
74                 void *buffer = jack_port_get_buffer (_jack_output_port, nframes);
75                 jack_midi_clear_buffer (buffer);
76                 flush (buffer); 
77         }
78         
79         if (_jack_input_port != 0) {
80                 // input
81                 void* jack_buffer = jack_port_get_buffer(_jack_input_port, nframes);
82                 const nframes_t event_count = jack_midi_get_event_count(jack_buffer);
83
84                 jack_midi_event_t ev;
85                 nframes_t cycle_start_frame = jack_last_frame_time (_jack_client);
86
87                 for (nframes_t i=0; i < event_count; ++i) {
88
89                         jack_midi_event_get (&ev, jack_buffer, i);
90
91                         if (input_parser) {
92                                 for (size_t i = 0; i < ev.size; i++) {
93                                         input_parser->set_timestamp (cycle_start_frame + ev.time);
94                                         input_parser->scanner (ev.buffer[i]);
95                                 }       
96                         }
97                 }       
98         }
99 }
100
101 void
102 JACK_MidiPort::cycle_end ()
103 {
104         if (_jack_output_port != 0) {
105                 flush (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle));
106         }
107 }
108
109 int
110 JACK_MidiPort::write(byte * msg, size_t msglen, timestamp_t timestamp)
111 {
112         int ret = 0;
113
114         if (!is_process_thread()) {
115
116                 Glib::Mutex::Lock lm (non_process_thread_fifo_lock);
117                 RingBuffer< Evoral::Event<double> >::rw_vector vec;
118                 
119                 non_process_thread_fifo.get_write_vector (&vec);
120
121                 if (vec.len[0] + vec.len[1] < 1) {
122                         error << "no space in FIFO for non-process thread MIDI write" << endmsg;
123                         return 0;
124                 }
125
126                 if (vec.len[0]) {
127                         vec.buf[0]->set (msg, msglen, timestamp);
128                 } else {
129                         vec.buf[1]->set (msg, msglen, timestamp);
130                 }
131
132                 non_process_thread_fifo.increment_write_idx (1);
133                 
134                 ret = msglen;
135                                 
136         } else {
137
138                 assert(_jack_output_port);
139                 
140                 // XXX This had to be temporarily commented out to make export work again
141                 if (!(timestamp < _nframes_this_cycle)) {
142                         std::cerr << "assertion timestamp < _nframes_this_cycle failed!" << std::endl;
143                 }
144
145                 if (_currently_in_cycle) {
146                         if (timestamp == 0) {
147                                 timestamp = _last_write_timestamp;
148                         } 
149
150                         if (jack_midi_event_write (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle), 
151                                                 timestamp, msg, msglen) == 0) {
152                                 ret = msglen;
153                                 _last_write_timestamp = timestamp;
154
155                         } else {
156                                 ret = 0;
157                                 cerr << "write of " << msglen << " failed, port holds "
158                                         << jack_midi_get_event_count (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle))
159                                         << endl;
160                         }
161                 } else {
162                         cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
163                 }
164         }
165
166         if (ret > 0 && output_parser) {
167                 output_parser->raw_preparse (*output_parser, msg, ret);
168                 for (int i = 0; i < ret; i++) {
169                         output_parser->scanner (msg[i]);
170                 }
171                 output_parser->raw_postparse (*output_parser, msg, ret);
172         }       
173
174         return ret;
175 }
176
177 void
178 JACK_MidiPort::flush (void* jack_port_buffer)
179 {
180         RingBuffer< Evoral::Event<double> >::rw_vector vec;
181         size_t written;
182
183         non_process_thread_fifo.get_read_vector (&vec);
184
185         if (vec.len[0] + vec.len[1]) {
186                 // cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n";
187         }
188
189         if (vec.len[0]) {
190                 Evoral::Event<double>* evp = vec.buf[0];
191                 
192                 for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
193                         jack_midi_event_write (jack_port_buffer,
194                                                (timestamp_t) evp->time(), evp->buffer(), evp->size());
195                 }
196         }
197         
198         if (vec.len[1]) {
199                 Evoral::Event<double>* evp = vec.buf[1];
200
201                 for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
202                         jack_midi_event_write (jack_port_buffer,
203                                                (timestamp_t) evp->time(), evp->buffer(), evp->size());
204                 }
205         }
206         
207         if ((written = vec.len[0] + vec.len[1]) != 0) {
208                 non_process_thread_fifo.increment_read_idx (written);
209         }
210 }
211
212 int
213 JACK_MidiPort::read(byte * buf, size_t bufsize)
214 {
215         cerr << "This program is improperly written. JACK_MidiPort::read() should never be called\n";
216         abort ();
217 }
218
219 int
220 JACK_MidiPort::create_ports(const XMLNode& node)
221 {
222         Descriptor desc (node);
223
224         assert(!_jack_input_port);
225         assert(!_jack_output_port);
226         
227         jack_nframes_t nframes = jack_get_buffer_size(_jack_client);
228
229         bool ret = true;
230
231         if (desc.mode == O_RDWR || desc.mode == O_WRONLY) {
232                 _jack_output_port = jack_port_register(_jack_client,
233                                                        string(desc.tag).append("_out").c_str(),
234                                                        JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
235                 if (_jack_output_port) {
236                         jack_midi_clear_buffer(jack_port_get_buffer(_jack_output_port, nframes));
237                 }
238                 ret = ret && (_jack_output_port != NULL);
239         }
240         
241         if (desc.mode == O_RDWR || desc.mode == O_RDONLY) {
242                 _jack_input_port = jack_port_register(_jack_client,
243                                                       string(desc.tag).append("_in").c_str(),
244                                                       JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
245                 if (_jack_input_port) {
246                         jack_midi_clear_buffer(jack_port_get_buffer(_jack_input_port, nframes));
247                 }
248                 ret = ret && (_jack_input_port != NULL);
249         }
250
251         return ret ? 0 : -1;
252 }
253
254 XMLNode& 
255 JACK_MidiPort::get_state () const
256 {
257         XMLNode& root (Port::get_state ());
258         return root;
259 }
260
261 void
262 JACK_MidiPort::set_state (const XMLNode& /*node*/)
263 {
264 }
265
266 void
267 JACK_MidiPort::set_process_thread (pthread_t thr)
268 {
269         _process_thread = thr;
270 }
271
272 bool
273 JACK_MidiPort::is_process_thread()
274 {
275         return (pthread_self() == _process_thread);
276 }