enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / backends / portaudio / winmmemidi_io.cc
1 /*
2  * Copyright (C) 2015 Tim Mayberry <mojofunk@gmail.com>
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 #include <windows.h>
20 #include <mmsystem.h>
21 #include <glibmm.h>
22
23 #include <sstream>
24 #include <set>
25
26 #include "pbd/error.h"
27 #include "pbd/compose.h"
28 #include "pbd/windows_timer_utils.h"
29
30 #include "winmmemidi_io.h"
31 #include "debug.h"
32
33 #include "pbd/i18n.h"
34
35 using namespace ARDOUR;
36
37 WinMMEMidiIO::WinMMEMidiIO()
38         : m_active (false)
39         , m_enabled (true)
40         , m_run (false)
41         , m_changed_callback (0)
42         , m_changed_arg (0)
43 {
44         pthread_mutex_init (&m_device_lock, 0);
45 }
46
47 WinMMEMidiIO::~WinMMEMidiIO()
48 {
49         pthread_mutex_lock (&m_device_lock);
50         cleanup();
51         pthread_mutex_unlock (&m_device_lock);
52         pthread_mutex_destroy (&m_device_lock);
53 }
54
55 void
56 WinMMEMidiIO::cleanup()
57 {
58         DEBUG_MIDI ("MIDI cleanup\n");
59         m_active = false;
60
61         destroy_input_devices ();
62         destroy_output_devices ();
63 }
64
65 bool
66 WinMMEMidiIO::dequeue_input_event (uint32_t port,
67                                    uint64_t timestamp_start,
68                                    uint64_t timestamp_end,
69                                    uint64_t &timestamp,
70                                    uint8_t *d,
71                                    size_t &s)
72 {
73         if (!m_active) {
74                 return false;
75         }
76         assert(port < m_inputs.size());
77
78         // m_inputs access should be protected by trylock
79         return m_inputs[port]->dequeue_midi_event (
80             timestamp_start, timestamp_end, timestamp, d, s);
81 }
82
83 bool
84 WinMMEMidiIO::enqueue_output_event (uint32_t port,
85                                     uint64_t timestamp,
86                                     const uint8_t *d,
87                                     const size_t s)
88 {
89         if (!m_active) {
90                 return false;
91         }
92         assert(port < m_outputs.size());
93
94         // m_outputs access should be protected by trylock
95         return m_outputs[port]->enqueue_midi_event (timestamp, d, s);
96 }
97
98
99 std::string
100 WinMMEMidiIO::port_id (uint32_t port, bool input)
101 {
102         std::stringstream ss;
103
104         if (input) {
105                 ss << "system:midi_capture_";
106                 ss << port;
107         } else {
108                 ss << "system:midi_playback_";
109                 ss << port;
110         }
111         return ss.str();
112 }
113
114 std::string WinMMEMidiIO::port_name(uint32_t port, bool input)
115 {
116         if (input) {
117                 if (port < m_inputs.size ()) {
118                         return m_inputs[port]->name ();
119                 }
120         } else {
121                 if (port < m_outputs.size ()) {
122                         return m_outputs[port]->name ();
123                 }
124         }
125         return "";
126 }
127
128 void
129 WinMMEMidiIO::start ()
130 {
131         if (m_run) {
132                 DEBUG_MIDI ("MIDI driver already started\n");
133                 return;
134         }
135
136         m_run = true;
137         DEBUG_MIDI ("Starting MIDI driver\n");
138
139         PBD::MMTIMERS::set_min_resolution();
140         discover();
141         start_devices ();
142 }
143
144
145 void
146 WinMMEMidiIO::stop ()
147 {
148         if (!m_run) {
149                 DEBUG_MIDI ("MIDI driver already stopped\n");
150                 return;
151         }
152         DEBUG_MIDI ("Stopping MIDI driver\n");
153         m_run = false;
154         stop_devices ();
155         pthread_mutex_lock (&m_device_lock);
156         cleanup ();
157         pthread_mutex_unlock (&m_device_lock);
158
159         PBD::MMTIMERS::reset_resolution();
160 }
161
162 void
163 WinMMEMidiIO::start_devices ()
164 {
165         for (std::vector<WinMMEMidiInputDevice*>::iterator i = m_inputs.begin ();
166              i < m_inputs.end();
167              ++i) {
168                 if (!(*i)->start ()) {
169                         PBD::error << string_compose (_("Unable to start MIDI input device %1\n"),
170                                                       (*i)->name ()) << endmsg;
171                 }
172         }
173         for (std::vector<WinMMEMidiOutputDevice*>::iterator i = m_outputs.begin ();
174              i < m_outputs.end();
175              ++i) {
176                 if (!(*i)->start ()) {
177                         PBD::error << string_compose (_ ("Unable to start MIDI output device %1\n"),
178                                                       (*i)->name ()) << endmsg;
179                 }
180         }
181 }
182
183 void
184 WinMMEMidiIO::stop_devices ()
185 {
186         for (std::vector<WinMMEMidiInputDevice*>::iterator i = m_inputs.begin ();
187              i < m_inputs.end();
188              ++i) {
189                 if (!(*i)->stop ()) {
190                         PBD::error << string_compose (_ ("Unable to stop MIDI input device %1\n"),
191                                                       (*i)->name ()) << endmsg;
192                 }
193         }
194         for (std::vector<WinMMEMidiOutputDevice*>::iterator i = m_outputs.begin ();
195              i < m_outputs.end();
196              ++i) {
197                 if (!(*i)->stop ()) {
198                         PBD::error << string_compose (_ ("Unable to stop MIDI output device %1\n"),
199                                                       (*i)->name ()) << endmsg;
200                 }
201         }
202 }
203
204 void
205 WinMMEMidiIO::clear_device_info ()
206 {
207         for (std::vector<MidiDeviceInfo*>::iterator i = m_device_info.begin();
208              i != m_device_info.end();
209              ++i) {
210           delete *i;
211         }
212         m_device_info.clear();
213 }
214
215 bool
216 WinMMEMidiIO::get_input_name_from_index (int index, std::string& name)
217 {
218         MIDIINCAPS capabilities;
219         MMRESULT result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities));
220
221         if (result == MMSYSERR_NOERROR) {
222                 DEBUG_MIDI(string_compose("Input Device: name : %1, mid : %2, pid : %3\n",
223                                           capabilities.szPname,
224                                           capabilities.wMid,
225                                           capabilities.wPid));
226
227                 name = Glib::locale_to_utf8 (capabilities.szPname);
228                 return true;
229         } else {
230                 DEBUG_MIDI ("Unable to get WinMME input device capabilities\n");
231         }
232         return false;
233 }
234
235 bool
236 WinMMEMidiIO::get_output_name_from_index (int index, std::string& name)
237 {
238         MIDIOUTCAPS capabilities;
239         MMRESULT result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities));
240         if (result == MMSYSERR_NOERROR) {
241                 DEBUG_MIDI(string_compose("Output Device: name : %1, mid : %2, pid : %3\n",
242                                           capabilities.szPname,
243                                           capabilities.wMid,
244                                           capabilities.wPid));
245
246                 name = Glib::locale_to_utf8 (capabilities.szPname);
247                 return true;
248         } else {
249                 DEBUG_MIDI ("Unable to get WinMME output device capabilities\n");
250         }
251         return false;
252 }
253
254 void
255 WinMMEMidiIO::update_device_info ()
256 {
257         std::set<std::string> device_names;
258
259         int in_count = midiInGetNumDevs ();
260
261         for (int i = 0; i < in_count; ++i) {
262                 std::string input_name;
263                 if (get_input_name_from_index(i, input_name)) {
264                         device_names.insert(input_name);
265                 }
266         }
267
268         int out_count = midiOutGetNumDevs ();
269
270         for (int i = 0; i < out_count; ++i) {
271                 std::string output_name;
272                 if (get_output_name_from_index(i, output_name)) {
273                         device_names.insert(output_name);
274                 }
275         }
276
277         clear_device_info ();
278
279         for (std::set<std::string>::const_iterator i = device_names.begin();
280              i != device_names.end();
281              ++i) {
282           m_device_info.push_back(new MidiDeviceInfo(*i));
283         }
284 }
285
286 MidiDeviceInfo*
287 WinMMEMidiIO::get_device_info (const std::string& name)
288 {
289         for (std::vector<MidiDeviceInfo*>::const_iterator i = m_device_info.begin();
290              i != m_device_info.end();
291              ++i) {
292                 if ((*i)->device_name == name) {
293                         return *i;
294                 }
295         }
296         return 0;
297 }
298
299 void
300 WinMMEMidiIO::create_input_devices ()
301 {
302         int srcCount = midiInGetNumDevs ();
303
304         DEBUG_MIDI (string_compose ("MidiIn count: %1\n", srcCount));
305
306         for (int i = 0; i < srcCount; ++i) {
307                 std::string input_name;
308                 if (!get_input_name_from_index (i, input_name)) {
309                         DEBUG_MIDI ("Unable to get MIDI input name from index\n");
310                         continue;
311                 }
312
313                 MidiDeviceInfo* info = get_device_info (input_name);
314
315                 if (!info) {
316                         DEBUG_MIDI ("Unable to MIDI device info from name\n");
317                         continue;
318                 }
319
320                 if (!info->enable) {
321                         DEBUG_MIDI(string_compose(
322                             "MIDI input device %1 not enabled, not opening device\n", input_name));
323                         continue;
324                 }
325
326                 try {
327                         WinMMEMidiInputDevice* midi_input = new WinMMEMidiInputDevice (i);
328                         if (midi_input) {
329                                 m_inputs.push_back (midi_input);
330                         }
331                 }
332                 catch (...) {
333                         DEBUG_MIDI ("Unable to create MIDI input\n");
334                         continue;
335                 }
336         }
337 }
338 void
339 WinMMEMidiIO::create_output_devices ()
340 {
341         int dstCount = midiOutGetNumDevs ();
342
343         DEBUG_MIDI (string_compose ("MidiOut count: %1\n", dstCount));
344
345         for (int i = 0; i < dstCount; ++i) {
346                 std::string output_name;
347                 if (!get_output_name_from_index (i, output_name)) {
348                         DEBUG_MIDI ("Unable to get MIDI output name from index\n");
349                         continue;
350                 }
351
352                 MidiDeviceInfo* info = get_device_info (output_name);
353
354                 if (!info) {
355                         DEBUG_MIDI ("Unable to MIDI device info from name\n");
356                         continue;
357                 }
358
359                 if (!info->enable) {
360                         DEBUG_MIDI(string_compose(
361                             "MIDI output device %1 not enabled, not opening device\n", output_name));
362                         continue;
363                 }
364
365                 try {
366                         WinMMEMidiOutputDevice* midi_output = new WinMMEMidiOutputDevice(i);
367                         if (midi_output) {
368                                 m_outputs.push_back(midi_output);
369                         }
370                 } catch(...) {
371                         DEBUG_MIDI ("Unable to create MIDI output\n");
372                         continue;
373                 }
374         }
375 }
376
377 void
378 WinMMEMidiIO::destroy_input_devices ()
379 {
380         while (!m_inputs.empty ()) {
381                 WinMMEMidiInputDevice* midi_input = m_inputs.back ();
382                 // assert(midi_input->stopped ());
383                 m_inputs.pop_back ();
384                 delete midi_input;
385         }
386 }
387
388 void
389 WinMMEMidiIO::destroy_output_devices ()
390 {
391         while (!m_outputs.empty ()) {
392                 WinMMEMidiOutputDevice* midi_output = m_outputs.back ();
393                 // assert(midi_output->stopped ());
394                 m_outputs.pop_back ();
395                 delete midi_output;
396         }
397 }
398
399 void
400 WinMMEMidiIO::discover()
401 {
402         if (!m_run) {
403                 return;
404         }
405
406         if (pthread_mutex_trylock (&m_device_lock)) {
407                 return;
408         }
409
410         cleanup ();
411
412         create_input_devices ();
413         create_output_devices ();
414
415         if (!(m_inputs.size () || m_outputs.size ())) {
416                 DEBUG_MIDI ("No midi inputs or outputs\n");
417                 pthread_mutex_unlock (&m_device_lock);
418                 return;
419         }
420
421         DEBUG_MIDI (string_compose ("Discovered %1 inputs and %2 outputs\n",
422                                     m_inputs.size (),
423                                     m_outputs.size ()));
424
425         if (m_changed_callback) {
426                 m_changed_callback(m_changed_arg);
427         }
428
429         m_active = true;
430         pthread_mutex_unlock (&m_device_lock);
431 }