NOOP, remove trailing tabs/whitespace.
[ardour.git] / libs / backends / wavesaudio / waves_midi_device.cc
1 /*
2     Copyright (C) 2013 Waves Audio Ltd.
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 */
19
20 #include <iostream>
21
22 #include "pbd/error.h"
23 #include "pbd/debug.h"
24 #include "pbd/compose.h"
25 #include "pbd/stacktrace.h"
26
27 #include "waves_midi_device.h"
28 #include "waves_midi_event.h"
29
30 // use non-zero latency because we want output to be timestapmed
31 #define LATENCY 0
32
33 #define QUEUE_LENGTH 1024
34
35 using namespace ARDOUR;
36 using namespace PBD;
37
38 WavesMidiDevice::WavesMidiDevice (const std::string& device_name)
39     : _pm_input_id (pmNoDevice)
40     , _pm_output_id (pmNoDevice)
41     , _name (device_name)
42     , _input_queue (NULL)
43     , _output_queue (NULL)
44     , _input_pm_stream (NULL)
45     , _output_pm_stream (NULL)
46     , _incomplete_waves_midi_event (NULL)
47 {
48         _pm_input_id = _pm_output_id = pmNoDevice;
49         int count = Pm_CountDevices ();
50
51         for (int i = 0; i < count; i++) {
52
53                 const PmDeviceInfo* pm_device_info = Pm_GetDeviceInfo (i);
54
55                 if (pm_device_info == NULL) {
56                         continue;
57                 }
58                 if (name () == pm_device_info->name) {
59                         if (pm_device_info->input){
60                                 _pm_input_id = i;
61                         }
62                         if (pm_device_info->output){
63                                 _pm_output_id = i;
64                         }
65                 }
66         }
67 }
68
69 WavesMidiDevice::~WavesMidiDevice ()
70 {
71         DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::~WavesMidiDevice (): %1\n", name()));
72         close ();
73 }
74
75 int
76 WavesMidiDevice::open (PmTimeProcPtr time_proc, void* time_info)
77 {
78     DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::open (): %1", name ()));
79
80     if (is_input () ) {
81                 // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::open (): INPUT" << _pm_input_id << "-[" << name () <<  "]" << std::endl;
82
83                 if (!_input_pm_stream) {
84                         // create queue
85                         if (!_input_queue) {
86                                 // COMMENTED DBG LOGS */ std::cout << "    going to Pm_QueueCreate for INPUT: " << std::endl;
87                                 _input_queue = Pm_QueueCreate (QUEUE_LENGTH, sizeof (const WavesMidiEvent*));
88                                 // COMMENTED DBG LOGS */ std::cout << "    DONE : " << std::endl;
89                                 if (NULL == _input_queue) {
90                     std::cerr << "WavesMidiDevice::open (): _input_queue = Pm_QueueCreate () failed for " << _pm_input_id << "-[" << name () <<  "]!" << std::endl;
91                     return -1;
92                                 }
93                         }
94                         // create stream
95                         // COMMENTED DBG LOGS */ std::cout << "    going to Pm_OpenInput : " << std::endl;
96             if (pmNoError != Pm_OpenInput (&_input_pm_stream,
97                                             _pm_input_id,
98                                             NULL,
99                                             1024,
100                                             time_proc,
101                                             time_info)) {
102                                         // COMMENTED DBG LOGS */ std::cout << "    DONE : " << std::endl;
103                     char* err_msg = new char[256];
104                                         Pm_GetHostErrorText(err_msg, 256);
105                                         std::cerr << "WavesMidiDevice::open (): Pm_OpenInput () failed for " << _pm_input_id << "-[" << name () <<  "]!" << std::endl;
106                                         std::cerr << "    Port Midi Host Error: " << err_msg << std::endl;
107                                         close ();
108                     return -1;
109             }
110                         // COMMENTED DBG LOGS */ std::cout << "    DONE : " << std::endl;
111                 }
112         }
113
114         if (is_output () ) {
115                 // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::open (): OUTPUT" << _pm_output_id << "-[" << name () <<  "]" << std::endl;
116
117                 if (!_output_pm_stream) {
118                         // create queue
119                         if (!_output_queue) {
120                                 // COMMENTED DBG LOGS */ std::cout << "    going to Pm_QueueCreate for OUTPUT : " << std::endl;
121                                 _output_queue = Pm_QueueCreate (QUEUE_LENGTH, sizeof (const WavesMidiEvent*));
122                                 // COMMENTED DBG LOGS */ std::cout << "    DONE : " << std::endl;
123                                 if (NULL == _output_queue) {
124                                         std::cerr << "WavesMidiDevice::open (): _output_queue = Pm_QueueCreate () failed for " << _pm_output_id << "-[" << name () <<  "]!" << std::endl;
125                                         return -1;
126                                 }
127                         }
128                         // create stream
129                         // COMMENTED DBG LOGS */ std::cout << "    going to Pm_OpenOutput : " << std::endl;
130                         if (pmNoError != Pm_OpenOutput (&_output_pm_stream,
131                                                         _pm_output_id,
132                                                         NULL,
133                                                         1024,
134                                                         time_proc,
135                                                         time_info,
136                                                         LATENCY)) {
137                                 // COMMENTED DBG LOGS */ std::cout << "    DONE : " << std::endl;
138                                 char* err_msg = new char[256];
139                                 Pm_GetHostErrorText(err_msg, 256);
140                                 std::cerr << "WavesMidiDevice::open (): Pm_OpenOutput () failed for " << _pm_output_id << "-[" << name () <<  "]!" << std::endl;
141                                 std::cerr << "    Port Midi Host Error: " << err_msg << std::endl;
142                                 close ();
143                                 return -1;
144                         }
145                         // COMMENTED DBG LOGS */ std::cout << "    DONE : " << std::endl;
146                 }
147         }
148         return 0;
149 }
150
151 void
152 WavesMidiDevice::close ()
153 {
154         DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::close (): %1\n", name ()));
155         WavesMidiEvent *waves_midi_event;
156
157         // save _input_pm_stream and _output_pm_stream to local buf
158         PmStream* input_pm_stream = _input_pm_stream;
159         PmStream* output_pm_stream = _output_pm_stream;
160         _input_pm_stream = _output_pm_stream = NULL;
161
162         // input
163         if (input_pm_stream) {
164                 // close stream
165                 PmError err = Pm_Close (input_pm_stream);
166                 if (err != pmNoError) {
167                         char* err_msg = new char[256];
168                         Pm_GetHostErrorText(err_msg, 256);
169                         std::cerr << "WavesMidiDevice::close (): Pm_Close (input_pm_stream) failed (" << err << ") for " << input_pm_stream << "-[" << name () <<  "]!" << std::endl;
170                         std::cerr << "    Port Midi Host Error: " << err_msg << std::endl;
171                 }
172                 _pm_input_id = pmNoDevice;
173         }
174
175         // close queue
176         if (_input_queue) {
177                 while (1 == Pm_Dequeue (_input_queue, &waves_midi_event)) {
178                         delete waves_midi_event; // XXX possible dup free in ~WavesMidiBuffer() (?)
179                 }
180                 Pm_QueueDestroy (_input_queue);
181                 _input_queue = NULL;
182         }
183
184         // output
185         if ( output_pm_stream ) {
186                 // close stream
187                 PmError err = Pm_Close (output_pm_stream);
188                 if (err != pmNoError) {
189                         char* err_msg = new char[256];
190                         Pm_GetHostErrorText(err_msg, 256);
191                         std::cerr << "WavesMidiDevice::close (): Pm_Close (output_pm_stream) failed (" << err << ") for " << output_pm_stream << "-[" << name () <<  "]!" << std::endl;
192                         std::cerr << "    Port Midi Host Error: " << err_msg << std::endl;
193                 }
194                 _pm_output_id = pmNoDevice;
195         }
196
197         // close queue
198         if (_output_queue) {
199                 while (1 == Pm_Dequeue (_output_queue, &waves_midi_event)) {
200                         delete waves_midi_event; // XXX possible dup free in ~WavesMidiBuffer() (?)
201                 }
202                 Pm_QueueDestroy (_output_queue);
203                 _output_queue = NULL;
204         }
205 }
206
207 void
208 WavesMidiDevice::do_io ()
209 {
210         read_midi ();
211         write_midi ();
212 }
213
214 void
215 WavesMidiDevice::read_midi ()
216 {
217         if (NULL == _input_pm_stream) {
218                 return;
219         }
220
221         while (Pm_Poll (_input_pm_stream) > 0) {
222
223                 PmEvent pm_event; // just one message at a time
224                 int result = Pm_Read (_input_pm_stream, &pm_event, 1);
225
226                 if (result < 0) {
227                         DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("Pm_Read failed for (): [%1]\n", name()));
228                         break;
229                 }
230
231                 DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_read_midi (): [%1] evt-tm: %2\n", name(), pm_event.timestamp));
232
233                 if (_incomplete_waves_midi_event == NULL ) {
234                         DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_read_midi (): [%1] new incomplete_waves_midi_event\n", name()));
235                         _incomplete_waves_midi_event = new WavesMidiEvent (pm_event.timestamp);
236                 }
237
238                 WavesMidiEvent *nested_pm_event = _incomplete_waves_midi_event->append_data (pm_event);
239
240                 if (nested_pm_event) {
241                         Pm_Enqueue (_input_queue, &nested_pm_event);
242                         DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_read_midi (): [%1] : Pm_Enqueue (_input_queue, nested_pm_event)\n", name()));
243                 }
244
245                 switch ( _incomplete_waves_midi_event->state ()) {
246                 case WavesMidiEvent::BROKEN:
247                         delete _incomplete_waves_midi_event;
248                         _incomplete_waves_midi_event = NULL;
249                         DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_read_midi (): [%1] : case WavesMidiEvent::BROKEN:\n", name()));
250                         break;
251                 case WavesMidiEvent::COMPLETE:
252                         DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_read_midi (): [%1] : Pm_Enqueue (_input_queue, _incomplete_waves_midi_event); %3\n",  name (), _incomplete_waves_midi_event));
253
254                                                 if (pmNoError != Pm_Enqueue (_input_queue, &_incomplete_waves_midi_event) ) {
255                                                         char* err_msg = new char[256];
256                                                         Pm_GetHostErrorText(err_msg, 256);
257                                                         std::cerr << "WavesMidiDevice::read_midi (): Pm_Enqueue () failed for [" << name () <<  "]!" << std::endl;
258                                                         std::cerr << "Error: " << err_msg << std::endl;
259                                                 }
260
261                         _incomplete_waves_midi_event = NULL;
262                         break;
263                 default:
264                         break;
265                 }
266         }
267 }
268
269 void
270 WavesMidiDevice::write_midi ()
271 {
272         if (NULL == _output_pm_stream) {
273                 return;
274         }
275
276         PmError err;
277         WavesMidiEvent *waves_midi_event;
278
279         while (1 == Pm_Dequeue (_output_queue, &waves_midi_event)) {
280                 if (waves_midi_event->sysex ()) {
281                         // LATENCY compensation
282                         err = Pm_WriteSysEx (_output_pm_stream, waves_midi_event->timestamp () - LATENCY, waves_midi_event->data ());
283                         if (0 > err) {
284                                 std::cerr << "WavesMidiDevice::write_event_to_device (): [" << name () << "] Pm_WriteSysEx () failed (" << err << ")!" << std::endl;
285                         };
286                         DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_write_midi (): SYSEX used, ev->tm: %1", waves_midi_event->timestamp () - LATENCY));
287                 }
288                 else
289                 {
290                         err = Pm_WriteShort (_output_pm_stream, waves_midi_event->timestamp () - LATENCY, * (PmMessage*)waves_midi_event->data ());
291                         if (0 > err) {
292                                 error << "WavesMidiDevice::write_event_to_device (): [" << name () << "] Pm_WriteShort () failed (" << err << ")!" << endmsg;
293                         }
294                         DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::_write_midi (): SHORTMSG used, ev->tm: %1\n", waves_midi_event->timestamp () - LATENCY));
295                 }
296         }
297
298         return;
299 }
300
301 int
302 WavesMidiDevice::enqueue_output_waves_midi_event (const WavesMidiEvent* waves_midi_event)
303 {
304         DEBUG_TRACE (DEBUG::WavesMIDI, string_compose ("WavesMidiDevice::enqueue_output_waves_midi_event () [%1]\n", name()));
305
306         if (waves_midi_event == NULL) {
307                 error << "WavesMidiDevice::put_event_to_callback (): 'waves_midi_event' is NULL!" << endmsg;
308                 return -1;
309         }
310
311         PmError err = Pm_Enqueue (_output_queue, &waves_midi_event);
312
313         if (0 > err) {
314                 error << "WavesMidiDevice::put_event_to_callback (): Pm_Enqueue () failed (" << err << ")!" << endmsg;
315                 return -1;
316         };
317
318         return 0;
319 }
320
321 WavesMidiEvent*
322 WavesMidiDevice::dequeue_input_waves_midi_event ()
323 {
324         WavesMidiEvent* waves_midi_event;
325         if (Pm_Dequeue (_input_queue, &waves_midi_event) == 1) {
326                 return waves_midi_event;
327         }
328         return NULL;
329 }