Move Windows timer utility functions from PA backend into libpbd
[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
22 #include <sstream>
23
24 #include "pbd/error.h"
25 #include "pbd/compose.h"
26 #include "pbd/windows_timer_utils.h"
27
28 #include "winmmemidi_io.h"
29 #include "debug.h"
30
31 #include "i18n.h"
32
33 using namespace ARDOUR;
34
35 WinMMEMidiIO::WinMMEMidiIO()
36         : m_active (false)
37         , m_enabled (true)
38         , m_run (false)
39         , m_changed_callback (0)
40         , m_changed_arg (0)
41 {
42         pthread_mutex_init (&m_device_lock, 0);
43 }
44
45 WinMMEMidiIO::~WinMMEMidiIO()
46 {
47         pthread_mutex_lock (&m_device_lock);
48         cleanup();
49         pthread_mutex_unlock (&m_device_lock);
50         pthread_mutex_destroy (&m_device_lock);
51 }
52
53 void
54 WinMMEMidiIO::cleanup()
55 {
56         DEBUG_MIDI ("MIDI cleanup\n");
57         m_active = false;
58
59         destroy_input_devices ();
60         destroy_output_devices ();
61 }
62
63 bool
64 WinMMEMidiIO::dequeue_input_event (uint32_t port,
65                                    uint64_t timestamp_start,
66                                    uint64_t timestamp_end,
67                                    uint64_t &timestamp,
68                                    uint8_t *d,
69                                    size_t &s)
70 {
71         if (!m_active) {
72                 return false;
73         }
74         assert(port < m_inputs.size());
75
76         // m_inputs access should be protected by trylock
77         return m_inputs[port]->dequeue_midi_event (
78             timestamp_start, timestamp_end, timestamp, d, s);
79 }
80
81 bool
82 WinMMEMidiIO::enqueue_output_event (uint32_t port,
83                                     uint64_t timestamp,
84                                     const uint8_t *d,
85                                     const size_t s)
86 {
87         if (!m_active) {
88                 return false;
89         }
90         assert(port < m_outputs.size());
91
92         // m_outputs access should be protected by trylock
93         return m_outputs[port]->enqueue_midi_event (timestamp, d, s);
94 }
95
96
97 std::string
98 WinMMEMidiIO::port_id (uint32_t port, bool input)
99 {
100         std::stringstream ss;
101
102         if (input) {
103                 ss << "system:midi_capture_";
104                 ss << port;
105         } else {
106                 ss << "system:midi_playback_";
107                 ss << port;
108         }
109         return ss.str();
110 }
111
112 std::string
113 WinMMEMidiIO::port_name (uint32_t port, bool input)
114 {
115         if (input) {
116                 if (port < m_inputs.size ()) {
117                         return m_inputs[port]->name ();
118                 }
119         } else {
120                 if (port < m_outputs.size ()) {
121                         return m_outputs[port]->name ();
122                 }
123         }
124         return "";
125 }
126
127 void
128 WinMMEMidiIO::start ()
129 {
130         if (m_run) {
131                 DEBUG_MIDI ("MIDI driver already started\n");
132                 return;
133         }
134
135         m_run = true;
136         DEBUG_MIDI ("Starting MIDI driver\n");
137
138         PBD::MMTIMERS::set_min_resolution();
139         discover();
140         start_devices ();
141 }
142
143
144 void
145 WinMMEMidiIO::stop ()
146 {
147         if (!m_run) {
148                 DEBUG_MIDI ("MIDI driver already stopped\n");
149                 return;
150         }
151         DEBUG_MIDI ("Stopping MIDI driver\n");
152         m_run = false;
153         stop_devices ();
154         pthread_mutex_lock (&m_device_lock);
155         cleanup ();
156         pthread_mutex_unlock (&m_device_lock);
157
158         PBD::MMTIMERS::reset_resolution();
159 }
160
161 void
162 WinMMEMidiIO::start_devices ()
163 {
164         for (std::vector<WinMMEMidiInputDevice*>::iterator i = m_inputs.begin ();
165              i < m_inputs.end();
166              ++i) {
167                 if (!(*i)->start ()) {
168                         PBD::error << string_compose (_("Unable to start MIDI input device %1\n"),
169                                                       (*i)->name ()) << endmsg;
170                 }
171         }
172         for (std::vector<WinMMEMidiOutputDevice*>::iterator i = m_outputs.begin ();
173              i < m_outputs.end();
174              ++i) {
175                 if (!(*i)->start ()) {
176                         PBD::error << string_compose (_ ("Unable to start MIDI output device %1\n"),
177                                                       (*i)->name ()) << endmsg;
178                 }
179         }
180 }
181
182 void
183 WinMMEMidiIO::stop_devices ()
184 {
185         for (std::vector<WinMMEMidiInputDevice*>::iterator i = m_inputs.begin ();
186              i < m_inputs.end();
187              ++i) {
188                 if (!(*i)->stop ()) {
189                         PBD::error << string_compose (_ ("Unable to stop MIDI input device %1\n"),
190                                                       (*i)->name ()) << endmsg;
191                 }
192         }
193         for (std::vector<WinMMEMidiOutputDevice*>::iterator i = m_outputs.begin ();
194              i < m_outputs.end();
195              ++i) {
196                 if (!(*i)->stop ()) {
197                         PBD::error << string_compose (_ ("Unable to stop MIDI output device %1\n"),
198                                                       (*i)->name ()) << endmsg;
199                 }
200         }
201 }
202
203 void
204 WinMMEMidiIO::create_input_devices ()
205 {
206         int srcCount = midiInGetNumDevs ();
207
208         DEBUG_MIDI (string_compose ("MidiIn count: %1\n", srcCount));
209
210         for (int i = 0; i < srcCount; ++i) {
211                 try {
212                         WinMMEMidiInputDevice* midi_input = new WinMMEMidiInputDevice (i);
213                         if (midi_input) {
214                                 m_inputs.push_back (midi_input);
215                         }
216                 }
217                 catch (...) {
218                         DEBUG_MIDI ("Unable to create MIDI input\n");
219                         continue;
220                 }
221         }
222 }
223 void
224 WinMMEMidiIO::create_output_devices ()
225 {
226         int dstCount = midiOutGetNumDevs ();
227
228         DEBUG_MIDI (string_compose ("MidiOut count: %1\n", dstCount));
229
230         for (int i = 0; i < dstCount; ++i) {
231                 try {
232                         WinMMEMidiOutputDevice* midi_output = new WinMMEMidiOutputDevice(i);
233                         if (midi_output) {
234                                 m_outputs.push_back(midi_output);
235                         }
236                 } catch(...) {
237                         DEBUG_MIDI ("Unable to create MIDI output\n");
238                         continue;
239                 }
240         }
241 }
242
243 void
244 WinMMEMidiIO::destroy_input_devices ()
245 {
246         while (!m_inputs.empty ()) {
247                 WinMMEMidiInputDevice* midi_input = m_inputs.back ();
248                 // assert(midi_input->stopped ());
249                 m_inputs.pop_back ();
250                 delete midi_input;
251         }
252 }
253
254 void
255 WinMMEMidiIO::destroy_output_devices ()
256 {
257         while (!m_outputs.empty ()) {
258                 WinMMEMidiOutputDevice* midi_output = m_outputs.back ();
259                 // assert(midi_output->stopped ());
260                 m_outputs.pop_back ();
261                 delete midi_output;
262         }
263 }
264
265 void
266 WinMMEMidiIO::discover()
267 {
268         if (!m_run) {
269                 return;
270         }
271
272         if (pthread_mutex_trylock (&m_device_lock)) {
273                 return;
274         }
275
276         cleanup ();
277
278         create_input_devices ();
279         create_output_devices ();
280
281         if (!(m_inputs.size () || m_outputs.size ())) {
282                 DEBUG_MIDI ("No midi inputs or outputs\n");
283                 pthread_mutex_unlock (&m_device_lock);
284                 return;
285         }
286
287         DEBUG_MIDI (string_compose ("Discovered %1 inputs and %2 outputs\n",
288                                     m_inputs.size (),
289                                     m_outputs.size ()));
290
291         if (m_changed_callback) {
292                 m_changed_callback(m_changed_arg);
293         }
294
295         m_active = true;
296         pthread_mutex_unlock (&m_device_lock);
297 }