more changes to broken-out tempo code
[ardour.git] / libs / surfaces / maschine2 / maschine2.cc
1 /*
2  * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
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, or (at your option)
7  * 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 Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdint.h>
23 #include <unistd.h>
24 #include <pthread.h>
25
26 #include "pbd/compose.h"
27 #include "pbd/error.h"
28 #include "pbd/i18n.h"
29 #include "pbd/abstract_ui.cc" // instantiate template
30
31 #include "ardour/async_midi_port.h"
32 #include "ardour/audioengine.h"
33 #include "ardour/session.h"
34
35 #include "midi++/port.h"
36
37 #include "maschine2.h"
38
39 #include "m2_dev_mk2.h"
40 #include "m2_map_mk2.h"
41
42 #include "m2_dev_mikro.h"
43 #include "m2_map_mikro.h"
44
45 #include "canvas.h"
46
47 using namespace ARDOUR;
48 using namespace PBD;
49 using namespace ArdourSurface;
50
51 #if 1 // TEST
52 #include "gtkmm2ext/colors.h"
53 #include "canvas/line.h"
54 #include "canvas/rectangle.h"
55 #include "canvas/text.h"
56
57 #include "layout.h"
58 #include "ui_knob.h"
59 #include "ui_menu.h"
60
61 class TestLayout : public Maschine2Layout
62 {
63         public:
64                 TestLayout (Maschine2& m2, Session& s, std::string const & name)
65                         : Maschine2Layout (m2, s, name)
66                         , knob (0)
67                         , menu (0)
68                 {
69                         using namespace ArdourCanvas;
70                         bg = new ArdourCanvas::Rectangle (this);
71                         bg->set (ArdourCanvas::Rect (0, 0, display_width(), display_height()));
72                         bg->set_fill_color (0x000000ff);
73
74                         upper_line = new Line (this);
75                         upper_line->set (Duple (0, 16.5), Duple (display_width(), 16.5));
76                         upper_line->set_outline_color (0xffffffff);
77
78                         knob = new Maschine2Knob(&m2, this);
79                         knob->set_position (Duple (64 + 32, 32));
80                         if (_session.master_out ()) {
81                                 knob->set_controllable (_session.master_out ()->gain_control());
82                         }
83
84                         std::vector<std::string> strs;
85                         strs.push_back("T|sg1");
86                         strs.push_back("Test2asdjasdlkjasldkjasd");
87                         strs.push_back("Test3");
88                         strs.push_back("Test4");
89                         strs.push_back("Test5");
90                         menu = new Maschine2Menu(&m2, this, strs);
91                         menu->set_position (Duple (0, 19));
92
93                 }
94                 Maschine2Knob* get_knob () { return knob; }
95                 Maschine2Menu* get_menu () { return menu; }
96         private:
97                 ArdourCanvas::Rectangle* bg;
98                 ArdourCanvas::Line* upper_line;
99                 Maschine2Knob* knob;
100                 Maschine2Menu* menu;
101 };
102
103 static TestLayout* tl = NULL;
104 #endif
105
106 Maschine2::Maschine2 (ARDOUR::Session& s)
107         : ControlProtocol (s, string (X_("NI Maschine2")))
108         , AbstractUI<Maschine2Request> (name())
109         , _handle (0)
110         , _hw (0)
111         , _ctrl (0)
112         , _canvas (0)
113         , _maschine_type (Maschine)
114         , _master_state (MST_NONE)
115 {
116         if (hid_init()) {
117                 throw Maschine2Exception ("HIDAPI initialization failed");
118         }
119         run_event_loop ();
120 }
121
122 Maschine2::~Maschine2 ()
123 {
124         stop ();
125         hid_exit ();
126 }
127
128 void*
129 Maschine2::request_factory (uint32_t num_requests)
130 {
131         return request_buffer_factory (num_requests);
132 }
133
134 void
135 Maschine2::do_request (Maschine2Request* req)
136 {
137         if (req->type == CallSlot) {
138                 call_slot (MISSING_INVALIDATOR, req->the_slot);
139         } else if (req->type == Quit) {
140                 stop ();
141         }
142 }
143
144 int
145 Maschine2::set_active (bool yn)
146 {
147         if (yn == active()) {
148                 return 0;
149         }
150
151         if (yn) {
152                 if (start ()) {
153                         return -1;
154                 }
155         } else {
156                 if (stop ()) {
157                         return -1;
158                 }
159         }
160
161         ControlProtocol::set_active (yn);
162         return 0;
163 }
164
165 XMLNode&
166 Maschine2::get_state()
167 {
168         XMLNode& node (ControlProtocol::get_state());
169         return node;
170 }
171
172 int
173 Maschine2::set_state (const XMLNode & node, int version)
174 {
175         if (ControlProtocol::set_state (node, version)) {
176                 return -1;
177         }
178         return 0;
179 }
180
181 int
182 Maschine2::start ()
183 {
184         _maschine_type = Maschine;
185         _handle = hid_open (0x17cc, 0x1140, NULL); // Maschine
186
187 #if 0
188         if (!_handle) {
189                 if ((_handle = hid_open (0x17cc, 0x1300, NULL))) {
190                         _maschine_type = Studio;
191                 }
192         }
193 #endif
194
195         if (!_handle) {
196                 if ((_handle = hid_open (0x17cc, 0x1110, NULL))) {
197                         _maschine_type = Mikro;
198                 }
199         }
200         if (!_handle) {
201                 if ((_handle = hid_open (0x17cc, 0x1200, NULL))) {
202                         _maschine_type = Mikro;
203                 }
204         }
205
206         if (!_handle) {
207                 error << _("Cannot find or connect to Maschine2\n");
208                 return -1;
209         }
210
211         hid_set_nonblocking (_handle, 1);
212
213         _midi_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Maschine2 out"), true);
214         if (!_midi_out) {
215                 error << _("Cannot create Maschine2 PAD MIDI Port");
216                 stop ();
217                 return -1;
218         }
219
220         boost::dynamic_pointer_cast<AsyncMIDIPort>(_midi_out)->set_flush_at_cycle_start (true);
221         _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_midi_out).get();
222
223         switch (_maschine_type) {
224                 case Mikro:
225                         _hw = new Maschine2Mikro ();
226                         _ctrl = new M2MapMikro ();
227                         info << _("Maschine2 Mikro control surface intialized");
228                         break;
229                 case Maschine:
230                         _hw = new Maschine2Mk2 ();
231                         _ctrl = new M2MapMk2 ();
232                         info << _("Maschine2 control surface intialized");
233                         break;
234                 case Studio:
235                         error << _("Maschine2 Studio is not yet supported");
236                         stop ();
237                         return -1;
238                         break;
239         }
240
241         _canvas = new Maschine2Canvas (*this, _hw);
242         connect_signals ();
243
244         Glib::RefPtr<Glib::TimeoutSource> write_timeout = Glib::TimeoutSource::create (40);
245         write_connection = write_timeout->connect (sigc::mem_fun (*this, &Maschine2::dev_write));
246         write_timeout->attach (main_loop()->get_context());
247
248 #ifdef PLATFORM_WINDOWS
249         Glib::RefPtr<Glib::TimeoutSource> read_timeout = Glib::TimeoutSource::create (20);
250 #else
251         Glib::RefPtr<Glib::TimeoutSource> read_timeout = Glib::TimeoutSource::create (1);
252 #endif
253         read_connection = read_timeout->connect (sigc::mem_fun (*this, &Maschine2::dev_read));
254         read_timeout->attach (main_loop ()->get_context());
255
256 #if 1 // TEST
257         tl = new TestLayout (*this, *session, "test");
258         tl->get_menu ()->set_control (_ctrl->encoder (1));
259         tl->get_knob ()->set_control (_ctrl->encoder (2));
260 #endif
261
262         return 0;
263 }
264
265 int
266 Maschine2::stop ()
267 {
268         read_connection.disconnect ();
269         write_connection.disconnect ();
270
271         session_connections.drop_connections ();
272         button_connections.drop_connections ();
273
274         if (_handle && _hw) {
275                 _hw->clear ();
276                 _hw->write (_handle, NULL);
277         }
278
279         hid_close (_handle);
280         _handle = 0;
281
282         stop_event_loop ();
283
284         if (_midi_out) {
285                 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
286                 asp->drain (10000, 500000);
287
288                 AudioEngine::instance()->unregister_port (_midi_out);
289                 _midi_out.reset ((ARDOUR::Port*) 0);
290                 _output_port = 0;
291         }
292
293         delete _canvas;
294         delete _hw;
295         delete _ctrl;
296
297         _canvas = 0;
298         _hw = 0;
299         _ctrl = 0;
300         return 0;
301 }
302
303 void
304 Maschine2::thread_init ()
305 {
306         pthread_set_name (event_loop_name().c_str());
307         ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 1024);
308         PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 1024);
309
310         struct sched_param rtparam;
311         memset (&rtparam, 0, sizeof (rtparam));
312         rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
313         if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
314                 // do we care? not particularly.
315         }
316 }
317
318 void
319 Maschine2::run_event_loop ()
320 {
321         BaseUI::run ();
322 }
323
324 void
325 Maschine2::stop_event_loop ()
326 {
327         BaseUI::quit ();
328 }
329
330 bool
331 Maschine2::dev_read ()
332 {
333         _hw->read (_handle, _ctrl);
334         return true;
335 }
336
337 bool
338 Maschine2::dev_write ()
339 {
340         _hw->write (_handle, _ctrl);
341         return true;
342 }
343
344 // move to callbacks.c || M2Contols implementation
345 Maschine2Layout*
346 Maschine2::current_layout() const
347 {
348 #if 1 // TEST
349         return tl;
350 #else
351         return NULL;
352 #endif
353 }