Skeleton for NI Maschine2 Surface
[ardour.git] / libs / surfaces / maschine2 / m2_dev_mikro.cc
1 #include <math.h>
2
3 #include "pbd/compose.h"
4
5 #include "maschine2.h"
6 #include "m2controls.h"
7 #include "m2_dev_mikro.h"
8
9 #include <pangomm/fontdescription.h>
10
11 #include "images.h"
12
13 static size_t mikro_png_readoff = 0;
14
15 static Cairo::ErrorStatus maschine_png_read (unsigned char* d, unsigned int s) {
16         if (s + mikro_png_readoff > sizeof (mikro_png)) {
17                 return CAIRO_STATUS_READ_ERROR;
18         }
19         memcpy (d, &mikro_png[mikro_png_readoff], s);
20         mikro_png_readoff += s;
21         return CAIRO_STATUS_SUCCESS;
22 }
23
24 using namespace ArdourSurface;
25
26 Maschine2Mikro::Maschine2Mikro () : M2Device ()
27 {
28         _surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, 128, 64);
29         clear (true);
30 }
31
32 void
33 Maschine2Mikro::clear (bool splash)
34 {
35         M2Device::clear (splash);
36
37         memset (&ctrl_in, 0, sizeof (ctrl_in));
38         memset (pad, 0, sizeof (pad));
39
40         _lights[0] = 0xff;
41
42         for (int l = 0; l < 4; ++l) {
43                 _img[l][0] = 0xff;
44         }
45
46         Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create (_surface);
47         if (!splash) {
48                 mikro_png_readoff = 0;
49                 Cairo::RefPtr<Cairo::ImageSurface> sf = Cairo::ImageSurface::create_from_png_stream (sigc::ptr_fun (maschine_png_read));
50                 cr->set_source(sf, 0, 0);
51                 cr->paint ();
52         } else {
53                 cr->set_operator (Cairo::OPERATOR_CLEAR);
54                 cr->paint ();
55                 cr->set_operator (Cairo::OPERATOR_OVER);
56
57                 Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (cr);
58                 Pango::FontDescription fd ("Sans Bold 18px");
59                 layout->set_font_description (fd);
60                 layout->set_alignment (Pango::ALIGN_CENTER);
61
62                 layout->set_text (string_compose ("%1\n%2", PROGRAM_NAME, VERSIONSTRING));
63                 int tw, th;
64                 layout->get_pixel_size (tw, th);
65                 cr->move_to (128 - tw * 0.5, 32 - th * 0.5);
66                 cr->set_source_rgb (1, 1, 1);
67                 layout->show_in_cairo_context(cr);
68         }
69         //_surface->write_to_png ("/tmp/amaschine.png");
70 }
71
72 void
73 Maschine2Mikro::read (hid_device* handle, M2Contols* ctrl)
74 {
75         assert (ctrl);
76         while (true) {
77                 uint8_t buf[256];
78                 int res = hid_read (handle, buf, 256);
79                 if (res < 1) {
80                         return;
81                 }
82
83                 // TODO parse incrementally if chunked at 64
84
85                 if (res > 4 && buf[0] == 0x01) {
86                         memcpy (&ctrl_in, &buf[1], sizeof (ctrl_in));
87                         assign_controls (ctrl);
88                 }
89                 else if (res > 32 && buf[0] == 0x20) {
90                         for (unsigned int i = 0; i < 16; ++i) {
91                                 uint8_t v0 = buf[1 + 2 * i];
92                                 uint8_t v1 = buf[2 + 2 * i];
93                                 uint8_t p = (v1 & 0xf0) >> 4;
94                                 pad[p] = ((v1 & 0xf) << 8) | v0;
95                                 unsigned int pid = 15 - ((i & 0xc) + (3 - (i & 0x3)));
96                                 ctrl->pad (pid)->set_value (pad[p]);
97                         }
98                         // TODO read complete 65 byte msg, expect buf[33] == 0x00
99                 }
100         }
101 }
102
103 void
104 Maschine2Mikro::write (hid_device* handle, M2Contols* ctrl)
105 {
106         bump_blink ();
107         uint8_t buf[265];
108
109         //TODO double-buffer, send changes only if needed
110
111         /* 30 control buttons, 8-bit brightness,
112          * + 16 RGB pads
113          */
114         buf[0] = 0x80;
115         set_lights (ctrl, &buf[1]);
116         set_pads (ctrl, &buf[31]);
117         if (memcmp (_lights, buf, 79)) {
118                         hid_write (handle, buf, 79);
119                         memcpy (_lights, buf, 79);
120         }
121
122         if (_splashcnt < _splashtime ) {
123                 ++_splashcnt;
124         }
125         else if (! vblank () /* EMIT SIGNAL*/) {
126                 /* check clear/initial draw */
127                 if (_img[0][0] != 0xff) {
128                         return;
129                 }
130         }
131
132         /* display */
133         _surface->flush ();
134         const unsigned char* img = _surface->get_data ();
135         const int stride = _surface->get_stride ();
136         memset (buf, 0, 9);
137         buf[0] = 0xe0;
138         for (int l = 0; l < 4; ++l) {
139                 buf[1] = 32 * l;
140                 buf[5] = 0x20;
141                 buf[7] = 0x08;
142
143                 int y0 = l * 16;
144                 for (int p = 0; p < 256; ++p) {
145                         uint8_t v = 0;
146                         const int y = y0 + p / 16;
147                         for (int b = 0; b < 8; ++b) {
148                                 const int x = (p % 16) * 8 + b;
149                                 int off = y * stride + x * 4 /* ARGB32 */;
150                                 /* off + 0 == blue
151                                  * off + 1 == green
152                                  * off + 2 == red
153                                  * off + 3 == alpha
154                                  */
155                                 /* calculate lightness */
156                                 uint8_t l = std::max (img[off + 0], std::max (img[off + 1], img[off + 2]));
157                                 if (l > 0x7e) { // TODO: take alpha channel into account?!
158                                         v |= 1 << (7 - b);
159                                 }
160                         }
161                         buf[9 + p] = v;
162                 }
163                 if (memcmp (_img[l], buf, 265)) {
164                         hid_write (handle, buf, 265);
165                         memcpy (_img[l], buf, 265);
166                 }
167         }
168 }
169
170 void
171 Maschine2Mikro::assign_controls (M2Contols* ctrl) const
172 {
173         ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->set_active (ctrl_in.trs_shift ? true : false);
174         M2Contols::Modifier mod = ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active () ? M2Contols::ModShift : M2Contols::ModNone;
175
176         bool change = false;
177 #define ASSIGN(BTN, VAR) \
178         change |= ctrl->button (M2Contols:: BTN, mod)->set_active (ctrl_in. VAR ? true : false)
179
180         ASSIGN (BtnRestart,   trs_restart);
181         ASSIGN (BtnStepLeft,  trs_left);
182         ASSIGN (BtnStepRight, trs_right);
183         ASSIGN (BtnGrid,      trs_grid);
184         ASSIGN (BtnPlay,      trs_play);
185         ASSIGN (BtnRec,       trs_rec);
186         ASSIGN (BtnErase,     trs_erase);
187
188         ASSIGN (BtnGroupA,     group);
189         ASSIGN (BtnBrowse,     browse);
190         ASSIGN (BtnSampling,   sampling);
191         ASSIGN (BtnNoteRepeat, note_repeat);
192         ASSIGN (BtnWheel,      mst_wheel);
193
194         ASSIGN (BtnTop0, f1);
195         ASSIGN (BtnTop1, f1);
196         ASSIGN (BtnTop2, f3);
197
198         ASSIGN (BtnControl,    control);
199         ASSIGN (BtnNavigate,   navigate); // XXX
200         ASSIGN (BtnNavLeft,    nav_left);
201         ASSIGN (BtnNavRight,   nav_right);
202         ASSIGN (BtnEnter,      main);
203
204         ASSIGN (BtnScene,     pads_scene);
205         ASSIGN (BtnPattern,   pads_pattern);
206         ASSIGN (BtnPadMode,   pads_mode);
207         ASSIGN (BtnNavigate,  pads_navigate);
208         ASSIGN (BtnDuplicate, pads_duplicate);
209         ASSIGN (BtnSelect,    pads_select);
210         ASSIGN (BtnSolo,      pads_solo);
211         ASSIGN (BtnMute,      pads_mute);
212 #undef ASSIGN
213
214         change |= ctrl->encoder (0)->set_value (ctrl_in.mst_wheel_pos);
215
216         if (change && mod == M2Contols::ModShift) {
217                 M2ToggleHoldButton* btn = dynamic_cast<M2ToggleHoldButton*> (ctrl->button (M2Contols::BtnShift, M2Contols::ModNone));
218                 if (btn) {
219                         btn->unset_active_on_release ();
220                 }
221         }
222 }
223
224 #define LIGHT(BIT, BTN) \
225         b[BIT] = ctrl->button (M2Contols:: BTN, mod)->lightness (_blink_shade)
226
227 void
228 Maschine2Mikro::set_pads (M2Contols* ctrl, uint8_t* b) const
229 {
230         if (!ctrl) {
231                 memset (b, 0, 48);
232                 return;
233         }
234         for (unsigned int i = 0; i < 16; ++i) {
235                 unsigned int pid = 15 - ((i & 0xc) + (3 - (i & 0x3)));
236                 ctrl->pad (pid)->color (b[i * 3], b[1 + i * 3], b[2 + i * 3]);
237         }
238 }
239
240 void
241 Maschine2Mikro::set_lights (M2Contols* ctrl, uint8_t* b) const
242 {
243         if (!ctrl) {
244                 memset (b, 0, 29);
245                 return;
246         }
247         M2Contols::Modifier mod = ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active () ? M2Contols::ModShift : M2Contols::ModNone;
248
249         LIGHT ( 0, BtnTop0); // F1
250         LIGHT ( 1, BtnTop1); // F2
251         LIGHT ( 2, BtnTop2); // F3
252         LIGHT ( 3, BtnControl);
253         LIGHT ( 4, BtnNavigate); // XXX
254         LIGHT ( 5, BtnNavLeft);
255         LIGHT ( 6, BtnNavRight);
256         LIGHT ( 7, BtnEnter); // Main
257
258         const uint32_t rgb = ctrl->button (M2Contols::BtnGroupA, mod)->color (_blink_shade);
259         b[8]  = (rgb >>  0) & 0xff;
260         b[9]  = (rgb >>  8) & 0xff;
261         b[10] = (rgb >> 16) & 0xff;
262
263         LIGHT (11, BtnBrowse);
264         LIGHT (12, BtnSampling);
265         LIGHT (13, BtnNoteRepeat);
266
267         LIGHT (14, BtnRestart);
268         LIGHT (15, BtnStepLeft);
269         LIGHT (16, BtnStepRight);
270         LIGHT (17, BtnGrid);
271         LIGHT (18, BtnPlay);
272         LIGHT (19, BtnRec);
273         LIGHT (20, BtnErase);
274         LIGHT (21, BtnShift);
275
276         LIGHT (22, BtnScene);
277         LIGHT (23, BtnPattern);
278         LIGHT (24, BtnPadMode);
279         LIGHT (25, BtnNavigate);
280         LIGHT (26, BtnDuplicate);
281         LIGHT (27, BtnSelect);
282         LIGHT (28, BtnSolo);
283         LIGHT (29, BtnMute);
284 }