use new syntax for connecting to backend signals that enforces explicit connection...
[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 <cstring>
24 #include <cstdlib>
25
26 #include "pbd/error.h"
27 #include "pbd/compose.h"
28
29 #include "midi++/types.h"
30 #include "midi++/jack.h"
31
32 using namespace std;
33 using namespace MIDI;
34 using namespace PBD;
35
36 pthread_t JACK_MidiPort::_process_thread;
37
38 JACK_MidiPort::JACK_MidiPort(const XMLNode& node, jack_client_t* jack_client)
39         : Port(node)
40         , _jack_client(jack_client)
41         , _jack_input_port(NULL)
42         , _jack_output_port(NULL)
43         , _last_read_index(0)
44         , output_fifo (512)
45         , input_fifo (1024)
46 {
47         if (!create_ports (node)) {
48                 _ok = true;
49         }
50 }
51
52 JACK_MidiPort::~JACK_MidiPort()
53 {
54         if (_jack_input_port) {
55                 jack_port_unregister (_jack_client, _jack_input_port);
56                 _jack_input_port = 0;
57         }
58
59         if (_jack_output_port) {
60                 jack_port_unregister (_jack_client, _jack_input_port);
61                 _jack_input_port = 0;
62         }
63 }
64
65 void
66 JACK_MidiPort::cycle_start (nframes_t nframes)
67 {
68         Port::cycle_start(nframes);
69         assert(_nframes_this_cycle == nframes);
70         _last_read_index = 0;
71         _last_write_timestamp = 0;
72
73         if (_jack_output_port != 0) {
74                 // output
75                 void *buffer = jack_port_get_buffer (_jack_output_port, nframes);
76                 jack_midi_clear_buffer (buffer);
77                 flush (buffer); 
78         }
79         
80         if (_jack_input_port != 0) {
81                 // input
82                 void* jack_buffer = jack_port_get_buffer(_jack_input_port, nframes);
83                 const nframes_t event_count = jack_midi_get_event_count(jack_buffer);
84
85                 jack_midi_event_t ev;
86                 timestamp_t cycle_start_frame = jack_last_frame_time (_jack_client);
87
88                 for (nframes_t i = 0; i < event_count; ++i) {
89                         jack_midi_event_get (&ev, jack_buffer, i);
90                         input_fifo.write (cycle_start_frame + ev.time, (Evoral::EventType) 0, ev.size, ev.buffer);
91                 }       
92                 
93                 if (event_count) {
94                         xthread.wakeup ();
95                 }
96         }
97 }
98
99 void
100 JACK_MidiPort::cycle_end ()
101 {
102         if (_jack_output_port != 0) {
103                 flush (jack_port_get_buffer (_jack_output_port, _nframes_this_cycle));
104         }
105
106         Port::cycle_end();
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 (output_fifo_lock);
117                 RingBuffer< Evoral::Event<double> >::rw_vector vec;
118                 
119                 output_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                 output_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                 // ardour doesn't care about this and neither should your app, probably
168                 // output_parser->raw_preparse (*output_parser, msg, ret);
169                 for (int i = 0; i < ret; i++) {
170                         output_parser->scanner (msg[i]);
171                 }
172                 // ardour doesn't care about this and neither should your app, probably
173                 // output_parser->raw_postparse (*output_parser, msg, ret);
174         }       
175
176         return ret;
177 }
178
179 void
180 JACK_MidiPort::flush (void* jack_port_buffer)
181 {
182         RingBuffer< Evoral::Event<double> >::rw_vector vec;
183         size_t written;
184
185         output_fifo.get_read_vector (&vec);
186
187         if (vec.len[0] + vec.len[1]) {
188                 // cerr << "Flush " << vec.len[0] + vec.len[1] << " events from non-process FIFO\n";
189         }
190
191         if (vec.len[0]) {
192                 Evoral::Event<double>* evp = vec.buf[0];
193                 
194                 for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
195                         jack_midi_event_write (jack_port_buffer,
196                                                (timestamp_t) evp->time(), evp->buffer(), evp->size());
197                 }
198         }
199         
200         if (vec.len[1]) {
201                 Evoral::Event<double>* evp = vec.buf[1];
202
203                 for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
204                         jack_midi_event_write (jack_port_buffer,
205                                                (timestamp_t) evp->time(), evp->buffer(), evp->size());
206                 }
207         }
208         
209         if ((written = vec.len[0] + vec.len[1]) != 0) {
210                 output_fifo.increment_read_idx (written);
211         }
212 }
213
214 int
215 JACK_MidiPort::read (byte * buf, size_t bufsize)
216 {
217         timestamp_t time;
218         Evoral::EventType type;
219         uint32_t size;
220         byte buffer[input_fifo.capacity()];
221
222         while (input_fifo.read (&time, &type, &size, buffer)) {
223                 if (input_parser) {
224                         input_parser->set_timestamp (time);
225                         for (uint32_t i = 0; i < size; ++i) {
226                                 input_parser->scanner (buffer[i]);
227                         }
228                 }
229         }
230
231         return 0;
232 }
233
234 int
235 JACK_MidiPort::create_ports(const XMLNode& node)
236 {
237         Descriptor desc (node);
238
239         assert(!_jack_input_port);
240         assert(!_jack_output_port);
241         
242         jack_nframes_t nframes = jack_get_buffer_size(_jack_client);
243
244         bool ret = true;
245
246         if (desc.mode == O_RDWR || desc.mode == O_WRONLY) {
247                 _jack_output_port = jack_port_register(_jack_client,
248                                                        string(desc.tag).append("_out").c_str(),
249                                                        JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
250                 if (_jack_output_port) {
251                         jack_midi_clear_buffer(jack_port_get_buffer(_jack_output_port, nframes));
252                 }
253                 ret = ret && (_jack_output_port != NULL);
254         }
255         
256         if (desc.mode == O_RDWR || desc.mode == O_RDONLY) {
257                 _jack_input_port = jack_port_register(_jack_client,
258                                                       string(desc.tag).append("_in").c_str(),
259                                                       JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
260                 if (_jack_input_port) {
261                         jack_midi_clear_buffer(jack_port_get_buffer(_jack_input_port, nframes));
262                 }
263                 ret = ret && (_jack_input_port != NULL);
264         }
265
266         return ret ? 0 : -1;
267 }
268
269 XMLNode& 
270 JACK_MidiPort::get_state () const
271 {
272         XMLNode& root (Port::get_state ());
273         return root;
274 }
275
276 void
277 JACK_MidiPort::set_state (const XMLNode& /*node*/)
278 {
279 }
280
281 void
282 JACK_MidiPort::set_process_thread (pthread_t thr)
283 {
284         _process_thread = thr;
285 }
286
287 bool
288 JACK_MidiPort::is_process_thread()
289 {
290         return (pthread_self() == _process_thread);
291 }