1 /*
2 * tracker/PianoControl.cpp
3 *
4 * Copyright 2009 Peter Barth
5 *
6 * This file is part of Milkytracker.
7 *
8 * Milkytracker is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Milkytracker is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Milkytracker. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 #include "PianoControl.h"
24 #include "Screen.h"
25 #include "GraphicsAbstract.h"
26 #include "Font.h"
27 #include "ScrollBar.h"
28 #include "Piano.h"
29 #include "Tools.h"
30 #include "PatternTools.h"
31 #include "TrackerConfig.h"
32
33 #define SCROLLBARWIDTH SCROLLBUTTONSIZE
34
35 static const bool blackKeys[] =
36 {
37 false,
38 true,
39 false,
40 true,
41 false,
42 false,
43 true,
44 false,
45 true,
46 false,
47 true,
48 false
49 };
50
51 static const PPPoint positions[] =
52 {
53 PPPoint(1,17),
54 PPPoint(5,6),
55 PPPoint(9,17),
56 PPPoint(13,6),
57 PPPoint(17, 17),
58 PPPoint(25, 17),
59 PPPoint(29,6),
60 PPPoint(33, 17),
61 PPPoint(37,6),
62 PPPoint(41, 17),
63 PPPoint(45,6),
64 PPPoint(49, 17)
65 };
66
67 static const PPColor colors[] =
68 {
69 PPColor(0,0,0),
70 PPColor(255,255,255),
71 PPColor(0,0,0),
72 PPColor(255,255,255),
73 PPColor(0,0,0),
74 PPColor(0,0,0),
75 PPColor(255,255,255),
76 PPColor(0,0,0),
77 PPColor(255,255,255),
78 PPColor(0,0,0),
79 PPColor(255,255,255),
80 PPColor(0,0,0)
81 };
82
XMAX()83 pp_int32 PianoControl::XMAX()
84 {
85 return pianoBitmap->getBitmapWidth();
86 }
87
YMAX()88 pp_int32 PianoControl::YMAX()
89 {
90 return pianoBitmap->getBitmapHeight();
91 }
92
KEYWIDTH()93 pp_int32 PianoControl::KEYWIDTH()
94 {
95 return XMAX()/pianoBitmap->getBitmapLUTWidth();
96 }
97
PianoControl(pp_int32 id,PPScreen * parentScreen,EventListenerInterface * eventListener,const PPPoint & location,const PPSize & size,pp_uint8 numNotes,bool border)98 PianoControl::PianoControl(pp_int32 id,
99 PPScreen* parentScreen,
100 EventListenerInterface* eventListener,
101 const PPPoint& location,
102 const PPSize& size,
103 pp_uint8 numNotes,
104 bool border/*= true*/) :
105 PPControl(id, parentScreen, eventListener, location, size),
106 border(border),
107 NUMNOTES(numNotes),
108 borderColor(&ourOwnBorderColor),
109 mode(ModeEdit)
110 {
111 // default color
112 ourOwnBorderColor.set(192, 192, 192);
113
114 hScrollbar = new PPScrollbar(0, parentScreen, this, PPPoint(location.x, location.y + size.height - SCROLLBARWIDTH - 1), size.width - 1, true);
115
116 #ifndef __LOWRES__
117 xscale = 1;
118 yscale = 1;
119 pianoBitmap = PianoBitmapLarge::getInstance();
120 #else
121 xscale = 2;
122 yscale = 1;
123 pianoBitmap = PianoBitmapSmall::getInstance();
124 #endif
125
126 xMax = XMAX()*xscale;
127 yMax = YMAX()*yscale;
128
129 visibleWidth = size.width - 2;
130 visibleHeight = size.height - SCROLLBARWIDTH - 2;
131
132 adjustScrollbars();
133
134 startPos = 0;
135
136 caughtControl = NULL;
137 controlCaughtByLMouseButton = controlCaughtByRMouseButton = false;
138
139 nbu = new pp_uint8[NUMNOTES];
140 memset(nbu, 0, NUMNOTES);
141
142 keyState = new KeyState[NUMNOTES];
143
144 sampleIndex = 0;
145 }
146
~PianoControl()147 PianoControl::~PianoControl()
148 {
149 delete hScrollbar;
150
151 delete[] nbu;
152
153 delete[] keyState;
154 }
155
paint(PPGraphicsAbstract * g)156 void PianoControl::paint(PPGraphicsAbstract* g)
157 {
158 if (!isVisible())
159 return;
160
161 pp_int32 xOffset = 2;
162
163 pp_int32 yOffset = 2;
164
165 g->setRect(location.x, location.y, location.x + size.width, location.y + size.height);
166
167 if (border)
168 {
169 drawBorder(g, *borderColor);
170 }
171
172 g->setRect(location.x + 1, location.y + 1, location.x + size.width - 2, location.y + size.height - 2);
173
174 pp_int32 width = visibleWidth;
175
176 pp_int32 adder = 65536/xscale;
177
178 pp_int32 oy = location.y + 1;
179
180 // for black piano keys
181 PPColor colCorrect(TrackerConfig::colorThemeMain);
182 colCorrect.r<<=1; colCorrect.g<<=1; colCorrect.b<<=1;
183 // for white piano keys
184 PPColor colCorrect2(TrackerConfig::colorThemeMain);
185 colCorrect2.scale(1.5f, 1.5f, 1.6f);
186 pp_int32 avg = (colCorrect2.b+colCorrect2.g+colCorrect2.r) / 3;
187 PPColor colCorrect4(avg, avg, avg);
188
189 pp_int32 PIANO_LUT_WIDTH = pianoBitmap->getBitmapLUTWidth();
190 const pp_uint8* PIANO_LUT = pianoBitmap->getBitmapLUT();
191 const pp_uint8* PIANO = pianoBitmap->getBitmap();
192
193 const pp_int32* DIVLUT = pianoBitmap->getDIVLUT();
194 const pp_int32* MODLUT = pianoBitmap->getMODLUT();
195
196 const pp_int32 XMAX = this->XMAX();
197
198 for (pp_int32 y = 0; y < visibleHeight; y++)
199 {
200 pp_int32 ry = y/yscale;
201
202 pp_int32 offset = (ry*XMAX+(startPos/xscale));
203 pp_int32 ofs = 0;
204
205 const pp_uint8* src = PIANO_LUT + (ry*PIANO_LUT_WIDTH*3);
206
207 const pp_int32* divLutPtr = DIVLUT-ry*XMAX;
208 const pp_int32* modLutPtr = MODLUT-ry*XMAX;
209
210 pp_int32 ox = location.x + 1;
211 for (pp_int32 x = 0; x < width; x++)
212 {
213 pp_int32 sx = offset+(ofs>>16);
214 pp_int32 px = modLutPtr[sx];
215
216 pp_int32 c = src[px];
217 pp_int32 gr = src[px+1];
218 pp_int32 b = src[px+2];
219
220 if (c == 255 && gr == 0 && b == 0)
221 {
222 g->setColor(PIANO[sx], PIANO[sx], PIANO[sx]);
223 }
224 else
225 {
226 // color values equal/above 240
227 if (c >= 240) c-=240;
228
229 pp_int32 note = divLutPtr[sx] + c;
230
231 if (keyState[note].pressed)
232 {
233 if (keyState[note].muted)
234 {
235 if (colors[c].r)
236 g->setSafeColor((PIANO[sx]+(colCorrect4.r>>2)), (PIANO[sx]+(colCorrect4.g>>2)), (PIANO[sx]+(colCorrect4.b>>2)));
237 else
238 g->setSafeColor((PIANO[sx]*colCorrect4.r)>>8, (PIANO[sx]*colCorrect4.g)>>8, (PIANO[sx]*colCorrect4.b)>>8);
239 }
240 else
241 {
242 if (colors[c].r)
243 g->setSafeColor((PIANO[sx]+(colCorrect.r>>1)), (PIANO[sx]+(colCorrect.g>>1)), (PIANO[sx]+(colCorrect.b>>1)));
244 else
245 g->setSafeColor((PIANO[sx]*colCorrect2.r)>>8, (PIANO[sx]*colCorrect2.g)>>8, (PIANO[sx]*colCorrect2.b)>>8);
246 }
247 }
248 else
249 g->setColor(PIANO[sx], PIANO[sx], PIANO[sx]);
250 }
251 g->setPixel(ox, oy);
252 ofs+=adder;
253 ox++;
254 }
255 oy++;
256 }
257
258 float newXScale = pianoBitmap->getBitmapWidth() / PianoBitmapSmall::getInstance()->getBitmapWidth();
259 float newYScale = pianoBitmap->getBitmapHeight() / PianoBitmapSmall::getInstance()->getBitmapHeight();
260
261 if (mode == ModeEdit)
262 {
263 PPFont* font;
264
265 font = (xscale == 1 && pianoBitmap == PianoBitmapSmall::getInstance()) ?
266 PPFont::getFont(PPFont::FONT_TINY) : PPFont::getFont(PPFont::FONT_SYSTEM);
267
268 g->setFont(font);
269
270 xOffset = 0;
271 yOffset = -1;
272 if (xscale > 1)
273 {
274 xOffset = 2;
275 }
276 else if (pianoBitmap == PianoBitmapLarge::getInstance())
277 {
278 xOffset = 1;
279 }
280
281 if (yscale > 1)
282 {
283 yOffset = -2;
284 }
285 else if (pianoBitmap == PianoBitmapLarge::getInstance())
286 {
287 yOffset = 0;
288 }
289
290 for (pp_int32 i = 0; i < NUMNOTES; i++)
291 {
292 pp_int32 posx = location.x + 1 + (i/12)*(PIANO_LUT_WIDTH*xscale) - startPos;
293
294 //if (blackKeys[i%12] && pianoBitmap == PianoBitmapLarge::getInstance())
295 // posx;
296
297 g->setColor(colors[i%12]);
298
299 char str[3] = "-";
300
301 if (nbu[i] != 255)
302 PPTools::convertToHex(str, nbu[i], 1);
303
304 g->drawChar(str[0],
305 (pp_int32)(posx + (positions[i%12].x*xscale + xOffset)*newXScale),
306 (pp_int32)(location.y + 1 + (positions[i%12].y*yscale + yOffset)*newYScale));
307 }
308
309 }
310 else if (mode == ModePlay && (xscale >= 2 || pianoBitmap == PianoBitmapLarge::getInstance()))
311 {
312 PPFont* font = PPFont::getFont(PPFont::FONT_TINY);
313
314 g->setFont(font);
315
316 if (pianoBitmap == PianoBitmapLarge::getInstance())
317 {
318 xOffset = 1;
319 yOffset = 0;
320 }
321 else
322 {
323 xOffset = 1;
324 yOffset = -1;
325 }
326
327 for (pp_int32 i = 0; i < NUMNOTES; i++)
328 {
329 pp_int32 posx = location.x + 1 + (i/12)*(PIANO_LUT_WIDTH*xscale) - startPos;
330
331 if (blackKeys[i%12] && pianoBitmap == PianoBitmapLarge::getInstance())
332 posx--;
333
334 g->setColor(colors[i%12]);
335
336 char str[4]/* = "C#"*/;
337 PatternTools::getNoteName(str, i+1);
338 if (str[1] == '-')
339 {
340 str[1] = str[2];
341 str[2] = '\0';
342 xOffset = 0;
343 }
344 else
345 {
346 str[2] = '\0';
347 xOffset = 1;
348 }
349
350 pp_int32 correctx = (KEYWIDTH()*xscale) / 2 - font->getStrWidth(str) / 2 - 3;
351 if (correctx < 0)
352 correctx = 0;
353
354 xOffset += correctx;
355
356 g->drawString(str,
357 (pp_int32)(posx + (positions[i%12].x*xscale + xOffset)*newXScale),
358 (pp_int32)(location.y + 1 + (positions[i%12].y*yscale + yOffset)*newYScale));
359 }
360 }
361
362 hScrollbar->paint(g);
363
364 }
365
dispatchEvent(PPEvent * event)366 pp_int32 PianoControl::dispatchEvent(PPEvent* event)
367 {
368 if (eventListener == NULL)
369 return -1;
370
371 //if (!visible)
372 // return 0;
373
374 switch (event->getID())
375 {
376 case eRMouseDown:
377 {
378 PPPoint* p = (PPPoint*)event->getDataPtr();
379
380 if (hScrollbar->hit(*p))
381 {
382 if (controlCaughtByLMouseButton)
383 break;
384 controlCaughtByRMouseButton = true;
385 caughtControl = hScrollbar;
386 caughtControl->dispatchEvent(event);
387 }
388 break;
389 }
390
391 case eLMouseDown:
392 {
393 PPPoint* p = (PPPoint*)event->getDataPtr();
394
395 // Clicked on horizontal scrollbar -> route event to scrollbar and catch scrollbar control
396 if (hScrollbar->hit(*p))
397 {
398 if (controlCaughtByRMouseButton)
399 break;
400 controlCaughtByLMouseButton = true;
401 caughtControl = hScrollbar;
402 caughtControl->dispatchEvent(event);
403 }
404 // Clicked in client area
405 else
406 {
407
408 pp_int32 note = positionToNote(*p);
409
410 if (note != -1)
411 {
412
413 switch (mode)
414 {
415 case ModeEdit:
416 {
417 nbu[note] = (pp_uint8)sampleIndex & 0xf;
418 PPEvent e(eValueChanged, &nbu, sizeof(pp_uint8*));
419 eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
420 break;
421 }
422
423 case ModePlay:
424 {
425 currentSelectedNote = note;
426 PPEvent e(eSelection, ¬e, sizeof(note));
427 eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
428 break;
429 }
430 }
431
432 parentScreen->paintControl(this);
433 }
434 }
435
436 break;
437 }
438
439 case eRMouseUp:
440 {
441 controlCaughtByRMouseButton = false;
442 if (caughtControl && !controlCaughtByLMouseButton && !controlCaughtByRMouseButton)
443 {
444 caughtControl->dispatchEvent(event);
445 caughtControl = NULL;
446 break;
447 }
448 break;
449 }
450
451 case eLMouseUp:
452 if (caughtControl == NULL)
453 {
454 pp_int32 note = currentSelectedNote;
455
456 if (note != -1)
457 {
458
459 switch (mode)
460 {
461 case ModePlay:
462 {
463 pp_int32 v = (1 << 16) + currentSelectedNote;
464 PPEvent e(eSelection, &v, sizeof(v));
465 eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
466 break;
467 }
468 case ModeEdit:
469 break;
470 }
471
472 parentScreen->paintControl(this);
473 }
474 break;
475 }
476
477 controlCaughtByLMouseButton = false;
478 if (!controlCaughtByLMouseButton && !controlCaughtByRMouseButton)
479 {
480 caughtControl->dispatchEvent(event);
481 caughtControl = NULL;
482 }
483 break;
484
485 case eLMouseDrag:
486 {
487 if (caughtControl && controlCaughtByLMouseButton)
488 {
489 caughtControl->dispatchEvent(event);
490 break;
491 }
492
493 //positionToNote(*(PPPoint*)event->getDataPtr());
494
495 //parentScreen->paintControl(this);
496
497 pp_int32 note = positionToNote(*(PPPoint*)event->getDataPtr());
498
499 if (note != -1)
500 {
501
502 switch (mode)
503 {
504 case ModeEdit:
505 {
506 nbu[note] = (pp_uint8)sampleIndex & 0xf;
507
508 PPEvent e(eValueChanged, &nbu, sizeof(pp_uint8*));
509
510 eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
511 break;
512 }
513
514 case ModePlay:
515 {
516 if (note == currentSelectedNote)
517 break;
518
519 pp_int32 v = (1 << 16) + currentSelectedNote;
520 PPEvent e(eSelection, &v, sizeof(v));
521 eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e);
522
523 currentSelectedNote = note;
524 PPEvent e2(eSelection, ¬e, sizeof(note));
525 eventListener->handleEvent(reinterpret_cast<PPObject*>(this), &e2);
526 break;
527 }
528 }
529
530 parentScreen->paintControl(this);
531 }
532
533 break;
534 }
535
536 case eRMouseDrag:
537 {
538 if (caughtControl && controlCaughtByRMouseButton)
539 caughtControl->dispatchEvent(event);
540 break;
541 }
542
543 case eRMouseRepeat:
544 {
545 if (caughtControl && controlCaughtByRMouseButton)
546 caughtControl->dispatchEvent(event);
547 break;
548 }
549
550 case eMouseWheelMoved:
551 {
552 TMouseWheelEventParams* params = (TMouseWheelEventParams*)event->getDataPtr();
553
554 if ((params->deltaX > 0 || params->deltaY < 0) && hScrollbar)
555 {
556 PPEvent e(eBarScrollDown);
557 handleEvent(reinterpret_cast<PPObject*>(hScrollbar), &e);
558 }
559 else if ((params->deltaX < 0 || params->deltaY > 0) && hScrollbar)
560 {
561 PPEvent e(eBarScrollUp);
562 handleEvent(reinterpret_cast<PPObject*>(hScrollbar), &e);
563 }
564
565 event->cancel();
566
567 break;
568 }
569
570 default:
571 if (caughtControl == NULL)
572 break;
573
574 caughtControl->dispatchEvent(event);
575 break;
576
577 }
578
579 return 0;
580 }
581
handleEvent(PPObject * sender,PPEvent * event)582 pp_int32 PianoControl::handleEvent(PPObject* sender, PPEvent* event)
583 {
584 // Horizontal scrollbar, scroll up (=left)
585 if (sender == reinterpret_cast<PPObject*>(hScrollbar) &&
586 event->getID() == eBarScrollUp)
587 {
588 startPos-=KEYWIDTH()*xscale;
589 if (startPos < 0)
590 startPos = 0;
591
592 pp_int32 visibleItems = visibleWidth;
593
594 float v = (float)(getMaxWidth() - visibleItems);
595
596 hScrollbar->setBarPosition((pp_int32)(startPos*(65536.0f/v)));
597 }
598 // Horizontal scrollbar, scroll down (=right)
599 else if (sender == reinterpret_cast<PPObject*>(hScrollbar) &&
600 event->getID() == eBarScrollDown)
601 {
602 pp_int32 visibleItems = visibleWidth;
603
604 startPos+=KEYWIDTH()*xscale;
605 if (startPos + visibleItems >= (signed)getMaxWidth())
606 startPos = getMaxWidth() - visibleItems;
607
608 float v = (float)(getMaxWidth() - visibleItems);
609
610 hScrollbar->setBarPosition((pp_int32)(startPos*(65536.0f/v)));
611 }
612 // Horizontal scrollbar, position changed
613 else if (sender == reinterpret_cast<PPObject*>(hScrollbar) &&
614 event->getID() == eBarPosChanged)
615 {
616
617 float pos = hScrollbar->getBarPosition()/65536.0f;
618
619 pp_int32 visibleItems = visibleWidth;
620
621 float v = (float)(getMaxWidth() - visibleItems);
622
623 startPos = (pp_uint32)(v*pos);
624 }
625
626 parentScreen->paintControl(this);
627
628 return 0;
629 }
630
getMaxWidth()631 pp_int32 PianoControl::getMaxWidth()
632 {
633 return XMAX()*xscale;
634 }
635
adjustScrollbars()636 void PianoControl::adjustScrollbars()
637 {
638 float s = (float)visibleWidth / (float)getMaxWidth();
639
640 float olds = hScrollbar->getBarSize() / 65536.0f;
641
642 hScrollbar->setBarSize((pp_int32)(s*65536.0f), false);
643
644 s = hScrollbar->getBarSize() / 65536.0f;
645
646 float scale = s / olds;
647
648 float pos = hScrollbar->getBarPosition()/65536.0f;
649 hScrollbar->setBarPosition((pp_int32)(pos*scale*65536.0f));
650
651 pos = hScrollbar->getBarPosition()/65536.0f;
652
653 pp_int32 visibleItems = visibleWidth;
654
655 float v = (float)(getMaxWidth() - visibleItems);
656
657 startPos = (pp_uint32)(v*pos);
658
659 if (startPos < 0)
660 {
661 startPos = 0;
662 }
663
664 //pp_int32 entireSize = (horizontal?this->size.width:this->size.height) - SCROLLBUTTONSIZE*2;
665 }
666
setxScale(pp_int32 scale)667 void PianoControl::setxScale(pp_int32 scale)
668 {
669 xscale = scale;
670 adjustScrollbars();
671 assureNoteVisible(4*12);
672 }
673
setyScale(pp_int32 scale)674 void PianoControl::setyScale(pp_int32 scale)
675 {
676 yscale = scale;
677 }
678
setLocation(const PPPoint & location)679 void PianoControl::setLocation(const PPPoint& location)
680 {
681 PPControl::setLocation(location);
682
683 pp_int32 p = hScrollbar->getBarPosition();
684 pp_int32 s = hScrollbar->getBarSize();
685 delete hScrollbar;
686 hScrollbar = new PPScrollbar(0, parentScreen, this, PPPoint(location.x, location.y + size.height - SCROLLBARWIDTH - 1), size.width - 1, true);
687 hScrollbar->setBarSize(s);
688 hScrollbar->setBarPosition(p);
689 }
690
setSize(const PPSize & size)691 void PianoControl::setSize(const PPSize& size)
692 {
693 PPControl::setSize(size);
694 delete hScrollbar;
695
696 hScrollbar = new PPScrollbar(0, parentScreen, this, PPPoint(location.x, location.y + size.height - SCROLLBARWIDTH - 1), size.width - 1, true);
697
698 visibleWidth = size.width - 2;
699 visibleHeight = size.height - SCROLLBARWIDTH - 2;
700
701 adjustScrollbars();
702 assureNoteVisible(4*12);
703 }
704
setSampleTable(const pp_uint8 * nbu)705 void PianoControl::setSampleTable(const pp_uint8* nbu)
706 {
707 if (nbu == NULL)
708 {
709 memset(this->nbu, 0, NUMNOTES);
710 return;
711 }
712
713 memcpy(this->nbu, nbu, NUMNOTES);
714 }
715
pressNote(pp_int32 note,bool pressed,bool muted)716 void PianoControl::pressNote(pp_int32 note, bool pressed, bool muted/* = false*/)
717 {
718 if (note >= 0 && note < NUMNOTES)
719 {
720 keyState[note].pressed = pressed;
721 keyState[note].muted = muted;
722 }
723 }
724
getNoteState(pp_int32 note) const725 bool PianoControl::getNoteState(pp_int32 note) const
726 {
727 if (note >= 0 && note < NUMNOTES)
728 return keyState[note].pressed;
729 return false;
730 }
731
assureNoteVisible(pp_int32 note)732 void PianoControl::assureNoteVisible(pp_int32 note)
733 {
734 pp_int32 PIANO_LUT_WIDTH = pianoBitmap->getBitmapLUTWidth();
735
736 pp_int32 startPos = (PIANO_LUT_WIDTH*(note/12)+positions[note%12].x)*xscale;
737 float v = (float)(getMaxWidth() - visibleWidth);
738 hScrollbar->setBarPosition((pp_int32)(startPos*(65536.0f/v)));
739 float pos = hScrollbar->getBarPosition()/65536.0f;
740 startPos = (pp_uint32)(v*pos);
741 this->startPos = startPos;
742 }
743
positionToNote(PPPoint cp)744 pp_int32 PianoControl::positionToNote(PPPoint cp)
745 {
746
747 if (sampleIndex < 0)
748 return -1;
749
750 cp.x -= location.x + 1;
751 cp.y -= location.y + 1;
752
753 if (cp.x < 0 || cp.x >= visibleWidth || cp.y < 0 || cp.y >= visibleHeight)
754 return -1;
755
756 pp_int32 PIANO_LUT_WIDTH = pianoBitmap->getBitmapLUTWidth();
757 const pp_uint8* PIANO_LUT = pianoBitmap->getBitmapLUT();
758
759 cp.x/=xscale;
760 cp.y/=yscale;
761
762 cp.x+=startPos/xscale;
763
764 pp_int32 octave = cp.x / PIANO_LUT_WIDTH;
765 pp_int32 ox = cp.x % PIANO_LUT_WIDTH;
766
767 pp_int32 c = PIANO_LUT[(cp.y * PIANO_LUT_WIDTH + ox)*3];
768 pp_int32 g = PIANO_LUT[(cp.y * PIANO_LUT_WIDTH + ox)*3+1];
769 pp_int32 b = PIANO_LUT[(cp.y * PIANO_LUT_WIDTH + ox)*3+2];
770
771 if (c == 255 && g == 0 && b == 0)
772 return -1;
773
774 // color values equal/above 240
775 if (c >= 240) c-=240;
776
777 pp_int32 note = octave*12 + c;
778
779 if (note < 0 || note >= NUMNOTES)
780 return -1;
781
782 return note;
783 }
784