1 /*
2 * Komposter
3 *
4 * Copyright (c) 2010 Noora Halme et al. (see AUTHORS)
5 *
6 * This code is licensed under the GNU General Public
7 * License version 2. See LICENSE for full text.
8 *
9 * Pattern editor page
10 *
11 */
12
13 #include "pattern.h"
14
15 // button indexes
16 #define B_PREV 0
17 #define B_NEXT 1
18 #define B_SHORTER 2
19 #define B_LONGER 3
20 #define B_OCTUP 4
21 #define B_OCTDN 5
22 #define B_PREVSYN 6
23 #define B_SYNNAME 7
24 #define B_NEXTSYN 8
25 #define B_PATTPLAY 9
26 #define B_PATTLOAD 10
27 #define B_PATTSAVE 11
28
29 #define B_PATTCLEAR 12
30
31 #define B_COPY 13
32 #define B_PASTE 14
33
34 #define KB_WIDTH 40
35
36 #define PIANOROLL_Y 528.5
37 #define PIANOROLL_X KB_WIDTH+5.5
38
39 #define PIANOROLL_HEIGHT 336
40
41 #define PIANOROLL_CELLWIDTH 11
42
43 #define PIANOROLL_CELLHEIGHT 7
44 #define PIANOROLL_BEAT (PIANOROLL_CELL*4)
45
46 #define PIANOROLL_OCTAVES 6
47
48
49 // from modules.c
50 extern float pitch[MAX_SYNTH];
51 extern int accent[MAX_SYNTH];
52 extern int gate[MAX_SYNTH];
53
54 extern int audiomode; // from audio.c
55 extern unsigned long playpos;
56 extern unsigned int audiomode_flags;
57
58 // from sequencer.c
59 extern int bpm;
60 extern int beats_per_measure;
61 extern int beatdiv;
62
63 extern int csynth; // from synthesizer.c
64 extern char synthname[MAX_SYNTH][128]; // from synthesizer.c
65 extern char patchname[MAX_SYNTH][MAX_PATCHES][128]; // from patch.c
66
67 extern synthmodule mod[MAX_SYNTH][MAX_MODULES]; // ditto
68 extern int signalfifo[MAX_SYNTH][MAX_MODULES]; // module execution stack
69 extern int cpatch[MAX_SYNTH]; // selected patch for each synth
70
71
72 // from patch.c
73 extern char pianokeys[29];
74 extern int kpkeydown;
75
76
77 // black or white key
78 const unsigned char keycolor[12]={1,0,1,0,1,1,0,1,0,1,0,1};
79
80
81 u32 pattdata[MAX_PATTERN][MAX_PATTLENGTH];
82 u32 pattlen[MAX_PATTERN];
83
84 u32 patt_clipboard[MAX_PATTLENGTH];
85 u32 patt_clipboard_len;
86
87 int cpatt, coct;
88
89 int patt_ui[15];
90 int piano_hover, piano_note, piano_drag, piano_dragto;
91 int piano_start;
92 int slide_hover;
93 float slide_drag_xofs;
94 int slide_drag, slide_drag_start;
95 int patt_cpatch;
96
97 int piano_porta_drag, piano_porta_drag_len, piano_porta_drag_from;
98
99 // initialize pattern data to defaults
pattern_init()100 void pattern_init()
101 {
102 int i,j;
103
104 cpatt=0;
105 coct=3;
106 for(i=0;i<MAX_PATTERN;i++) {
107 pattlen[i]=4;
108 for(j=0;j<MAX_PATTLENGTH;j++) pattdata[i][j]=0;
109 }
110 piano_note=-1; piano_hover=-1; piano_drag=-1; piano_dragto=-1;
111 piano_start=0; slide_hover=0;
112 patt_cpatch=0;
113 piano_porta_drag=-1; piano_porta_drag_len=0;
114 for(i=0;i<13;i++) patt_ui[i]=0;
115 patt_clipboard_len=0;
116 }
117
118
pattern_toggleplayback()119 void pattern_toggleplayback()
120 {
121 int patt_playing;
122
123 patt_playing=((audiomode==AUDIOMODE_PATTERNPLAY)&1)^1;
124 patt_ui[B_PATTPLAY]&=1;
125 patt_ui[B_PATTPLAY]|=(patt_playing<<1);
126 playpos=0;
127 gate[0]=0;
128 if (patt_playing) {
129 audiomode=AUDIOMODE_PATTERNPLAY; audiomode_flags|=1;
130 } else {
131 audiomode=AUDIOMODE_COMPOSING;
132 }
133 }
134
135
136
137 // returns pattern offset the cursor is on top of or -1 if the cursor is currently
138 // off the piano roll. when a position is returned, the int pointed by note is set to
139 // the midi note number under the cursor.
pattern_cursorpos(int x,int y,int * note)140 int pattern_cursorpos(int x, int y, int *note)
141 {
142 int cx,cy;
143 float lineheight;
144
145 if (((coct+PIANOROLL_OCTAVES)*12)<MAX_NOTE) {
146 lineheight=PIANOROLL_OCTAVES*12;
147 } else {
148 lineheight=PIANOROLL_OCTAVES*12 - (((coct+PIANOROLL_OCTAVES)*12)-MAX_NOTE);
149 }
150
151 if (
152 x > PIANOROLL_X &&
153 x < (PIANOROLL_X + (pattlen[cpatt]*(beats_per_measure*beatdiv))*PIANOROLL_CELLWIDTH + 1) &&
154 y < PIANOROLL_Y + 3 &&
155 y > (PIANOROLL_Y + 1 - lineheight*PIANOROLL_CELLHEIGHT)
156 )
157 {
158 cx=(int)((x-PIANOROLL_X)/PIANOROLL_CELLWIDTH)-1;
159 cy=abs((int)((y-PIANOROLL_Y-2)/PIANOROLL_CELLHEIGHT));
160 if (note) *note=coct*12+cy;
161 return cx;
162 }
163 return -1;
164 }
165
166
pattern_mouse_hover(int x,int y)167 void pattern_mouse_hover(int x, int y)
168 {
169
170 // test buttons
171 patt_ui[B_PREV]=hovertest_box(x, y, 14, DS_HEIGHT-14, 16, 16);
172 patt_ui[B_NEXT]=hovertest_box(x, y, 64, DS_HEIGHT-14, 16, 16);
173 patt_ui[B_SHORTER]=hovertest_box(x, y, 98, DS_HEIGHT-14, 16, 16);
174 patt_ui[B_LONGER]=hovertest_box(x, y, 180, DS_HEIGHT-14, 16, 16);
175 patt_ui[B_OCTDN]=hovertest_box(x, y, 12, PIANOROLL_Y+16, 16, 16);
176 patt_ui[B_OCTUP]=hovertest_box(x, y, 32, PIANOROLL_Y+16, 16, 16);
177 patt_ui[B_PREVSYN]=hovertest_box(x, y, 214, DS_HEIGHT-14, 16, 16);
178 patt_ui[B_NEXTSYN]=hovertest_box(x, y, 430, DS_HEIGHT-14, 16, 16);
179
180 patt_ui[B_PATTCLEAR]=hovertest_box(x, y, 555, DS_HEIGHT-14, 16, 16) | (patt_ui[B_PATTCLEAR]&8);
181
182 patt_ui[B_COPY]=hovertest_box(x,y,622, DS_HEIGHT-14, 16, 16);
183 patt_ui[B_PASTE]=hovertest_box(x,y,644, DS_HEIGHT-14, 16, 16);
184 if (patt_clipboard_len==0) patt_ui[B_PASTE]=0;
185
186 patt_ui[B_PATTPLAY]=hovertest_box(x, y, 482, DS_HEIGHT-14, 16, 42);
187 if (audiomode==AUDIOMODE_PATTERNPLAY) patt_ui[B_PATTPLAY]|=2;
188
189 // hovering above piano roll?
190 piano_note=-1;
191 piano_hover=pattern_cursorpos(x,y,&piano_note);
192
193 // hovering above slider?
194 slide_hover=hovertest_hslider(x,y,PIANOROLL_X, PIANOROLL_Y+12, (DS_WIDTH-(PIANOROLL_X+6)), 12,
195 piano_start, (DS_WIDTH-(PIANOROLL_X+4))/PIANOROLL_CELLWIDTH, pattlen[cpatt]*(beats_per_measure*beatdiv));
196
197 // any ui-elements active?
198 //for(m=0;m<7;m++) if (patt_ui[m]&1) return;
199 }
200
201
pattern_mouse_drag(int x,int y)202 void pattern_mouse_drag(int x, int y)
203 {
204 int m,n,note,i;
205 float f, cos, cip, sbw, slw;
206
207 // dragging a new note, paint all cells from drag point to current cell
208 if (piano_drag >= 0) {
209 m=pattern_cursorpos(x, y, ¬e); // disregard note
210 if (m>=piano_drag) piano_dragto=m;
211 return;
212 }
213
214 // dragging the piano roll scrollbar
215 if (slide_drag>0) {
216 sbw=DS_WIDTH-(PIANOROLL_X+6); // scrollbar width
217 cos=(DS_WIDTH-(PIANOROLL_X))/PIANOROLL_CELLWIDTH; // cells on screen
218 cip=pattlen[cpatt]*(beats_per_measure*beatdiv); // cells in pattern
219 slw=sbw*(cos/cip);
220 f=((x-slide_drag_xofs)/(sbw-slw))*(cip-cos);
221 piano_start=slide_drag_start+f;
222 if (piano_start>(1+cip-cos)) piano_start=(1+cip-cos);
223 if (piano_start<0) piano_start=0;
224 return;
225 }
226
227 if (piano_porta_drag >= 0) {
228 n=pattdata[cpatt][piano_start+piano_porta_drag]; // current note being dragged
229 m=pattern_cursorpos(x, y, ¬e);
230 if (note != n) {
231 // replace the note on the pattern data
232 for(i=0;i<piano_porta_drag_len;i++) {
233 pattdata[cpatt][piano_start+piano_porta_drag+i]&=0xff80;
234 pattdata[cpatt][piano_start+piano_porta_drag+i]|=note&0xff;
235 }
236 piano_note=note&0xff;
237 //audio_trignote(0, piano_note); // audio feedback
238 }
239 }
240 }
241
242
pattern_mouse_click(int button,int state,int x,int y)243 void pattern_mouse_click(int button, int state, int x, int y)
244 {
245 int i,m,j,l;
246 char tmps[256];
247
248 if (button==GLUT_LEFT_BUTTON) {
249 if (state==GLUT_DOWN) {
250
251 // test first if piano roll is clicked with modifiers
252 m=glutGetModifiers();
253 if (m==GLUT_ACTIVE_SHIFT) {
254 if (piano_hover>=0 && piano_note==(pattdata[cpatt][piano_start+piano_hover]&0xff)) {
255 // wipe the note from begin to end
256 i=piano_start+piano_hover;
257 while(pattdata[cpatt][i]&NOTE_LEGATO) i--;
258 do { pattdata[cpatt][i++]=0;
259 } while(pattdata[cpatt][i]&NOTE_LEGATO);
260 return;
261 }
262 }
263
264 // clear the pattern
265 if (patt_ui[B_PATTCLEAR]&1) {
266 if (patt_ui[B_PATTCLEAR]&8) {
267 for(i=0;i<MAX_PATTLENGTH;i++) pattdata[cpatt][i]=0;
268 sprintf(tmps, "Pattern %02d cleared", cpatt);
269 console_post(tmps);
270 patt_ui[B_PATTCLEAR]&=0xff-8;
271 } else {
272 console_post("Click again to clear pattern");
273 patt_ui[B_PATTCLEAR]|=8;
274 }
275 return;
276 } else {
277 patt_ui[B_PATTCLEAR]&=0xff-8;
278 }
279
280 // click on the ui buttons
281 if (patt_ui[B_PREV]) { if (cpatt>0) cpatt--; return; }
282 if (patt_ui[B_NEXT]) { if (cpatt<MAX_PATTERN) cpatt++; return; }
283
284 if (patt_ui[B_SHORTER]) {
285 if (pattlen[cpatt]>1) {
286 // clear the pattern data no longer visible if shift down
287 m=glutGetModifiers();
288 if (m==GLUT_ACTIVE_SHIFT) {
289 for(i=((pattlen[cpatt]*(beats_per_measure*beatdiv))/2); i<(pattlen[cpatt]*(beats_per_measure*beatdiv)); i++) pattdata[cpatt][i]=0;
290 }
291
292 pattlen[cpatt]/=2;
293
294 while (piano_start >
295 ( 1+pattlen[cpatt]*(beats_per_measure*beatdiv) - ( (DS_WIDTH-(PIANOROLL_X))/PIANOROLL_CELLWIDTH ) ) ) piano_start--;
296 if (piano_start<0) piano_start=0;
297 }
298 return;
299 }
300
301 if (patt_ui[B_LONGER]) {
302 if (pattlen[cpatt]<16) {
303 pattlen[cpatt]*=2;
304
305 // duplicate first half of pattern to second half if shift pressed
306 m=glutGetModifiers();
307 if (m==GLUT_ACTIVE_SHIFT) {
308 for(i=0; i<((pattlen[cpatt]*(beats_per_measure*beatdiv))/2); i++) pattdata[cpatt][i+((pattlen[cpatt]*(beats_per_measure*beatdiv))/2)]=pattdata[cpatt][i];
309 }
310 }
311 return;
312 }
313
314 if (patt_ui[B_OCTDN]) { if (coct>0) coct--; return; }
315 if (patt_ui[B_OCTUP]) { if (coct<MAX_OCTAVE) coct++; return; }
316
317 if (patt_ui[B_PREVSYN]) { if (csynth[cpatch]>0) csynth[cpatch]--;
318 audio_loadpatch(0, csynth, cpatch[csynth]);
319 return; }
320 if (patt_ui[B_NEXTSYN]) { if (csynth[cpatch]<(MAX_PATCHES-1)) csynth[cpatch]++;
321 audio_loadpatch(0, csynth, cpatch[csynth]);
322 return; }
323
324 if (patt_ui[B_PATTPLAY]&1) pattern_toggleplayback();
325
326 if (patt_ui[B_COPY]) {
327 // copy pattern data to clipboard
328 for(i=0; i<pattlen[cpatt]*(beats_per_measure*beatdiv); i++) patt_clipboard[i]=pattdata[cpatt][i];
329 patt_clipboard_len=pattlen[cpatt]*(beats_per_measure*beatdiv);
330 console_post("Current pattern copied to clipboard");
331 }
332
333 if (patt_ui[B_PASTE] && patt_clipboard_len>0) {
334 for(i=0; i<patt_clipboard_len; i++) pattdata[cpatt][i]=patt_clipboard[i];
335 }
336
337 // click on the piano roll?
338 if (piano_hover>=0) {
339
340 if (piano_note==(pattdata[cpatt][piano_start+piano_hover]&0xff)) {
341 // clicked on an existing note, start drag up/down
342 j=piano_hover;
343 if (pattdata[cpatt][piano_start+j]&NOTE_LEGATO) {
344 while (pattdata[cpatt][piano_start+j]&NOTE_LEGATO) j--;
345 }
346 for(l=1; pattdata[cpatt][piano_start+j+l]&NOTE_LEGATO; l++);
347 piano_porta_drag=j;
348 piano_porta_drag_len=l;
349 piano_porta_drag_from=pattdata[cpatt][piano_start+j]&0xff;
350 } else {
351 // create a new note - set the 1/16th note on the pattern
352 pattdata[cpatt][piano_start+piano_hover]=piano_note;
353 piano_drag=piano_hover; //drag note stating from this cell
354 piano_dragto=piano_hover;
355
356 // trig the note to get an audible feedback
357 audio_trignote(0, piano_note);
358 }
359 }
360
361 // click on the slider?
362 if (slide_hover) {
363 slide_drag=1;
364 slide_drag_xofs=x;
365 slide_drag_start=piano_start;
366 }
367 }
368 if (state==GLUT_UP) {
369 if (slide_drag) { slide_drag=0; return; }
370 if (piano_drag>=0) {
371 // released the drag, draw the notes with legato and reset drag
372 for(m=piano_drag;m<=piano_dragto;m++) pattdata[cpatt][piano_start+m]=pattdata[cpatt][piano_start+piano_drag]|NOTE_LEGATO;
373 pattdata[cpatt][piano_start+piano_drag]&=0xff; // legato off from the first note
374 pattdata[cpatt][piano_start+piano_dragto+1]&=0xff; // legato off from the note which follows
375 piano_drag=-1;
376 piano_dragto=-1;
377
378 gate[0]=0; // note off
379 }
380 if (piano_porta_drag>=0) {
381 piano_porta_drag=-1;
382 piano_porta_drag_len=0;
383
384 gate[0]=0; // note off
385 }
386 }
387 }
388
389
390 if (button==GLUT_RIGHT_BUTTON) {
391 if (state==GLUT_DOWN) {
392
393 if (piano_hover>=0) {
394 // right click to set accent
395 if (pattdata[cpatt][piano_start+piano_hover]) { // got a note here
396 if (pattdata[cpatt][piano_start+piano_hover]&NOTE_LEGATO) {
397 //printf("legato here\n"); // do nothing
398 } else {
399 // first note, so mark it with an accent
400 pattdata[cpatt][piano_start+piano_hover]^=NOTE_ACCENT;
401 }
402 }
403 }
404
405
406 }
407 }
408 }
409
410
411 // keyboard callback
pattern_keyboard(unsigned char key,int x,int y)412 void pattern_keyboard(unsigned char key, int x, int y)
413 {
414 int i,pkey;
415
416 switch (key) {
417 case ' ': pattern_toggleplayback(); return; break;
418 case '-':
419 if (pattlen[cpatt]>1) pattlen[cpatt]/=2;
420 while (piano_start >
421 ( 1+pattlen[cpatt]*(beats_per_measure*beatdiv) - ( (DS_WIDTH-(PIANOROLL_X))/PIANOROLL_CELLWIDTH ) ) ) piano_start--;
422 if (piano_start<0) piano_start=0;
423 return;
424 break;
425 case '+':
426 if (pattlen[cpatt]<16) pattlen[cpatt]*=2;
427 return;
428 break;
429 case ',':
430 if (csynth[cpatch]>0) csynth[cpatch]--;
431 audio_loadpatch(0, csynth, cpatch[csynth]);
432 return;
433 break;
434 case '.':
435 if (csynth[cpatch]<(MAX_PATCHES-1)) csynth[cpatch]++;
436 audio_loadpatch(0, csynth, cpatch[csynth]);
437 return;
438 break;
439 }
440
441 // keyjazzing! w00t!
442 for (i=0;i<29;i++) {
443 if (pianokeys[i]==key) {
444 // trig note. use same base octave as pattern editor
445 pkey=coct*12 + i;
446 kpkeydown=pkey;
447 audio_trignote(0, pkey);
448 }
449 }
450 }
451
452
pattern_specialkey(unsigned char key,int x,int y)453 void pattern_specialkey(unsigned char key, int x, int y)
454 {
455 switch(key)
456 {
457 case GLUT_KEY_LEFT:
458 if (cpatt>0) cpatt--;
459 break;
460
461 case GLUT_KEY_RIGHT:
462 if (cpatt<MAX_PATTERN) cpatt++;
463 break;
464
465 case GLUT_KEY_DOWN:
466 if (coct>0) coct--;
467 break;
468
469 case GLUT_KEY_UP:
470 if (coct<(9-PIANOROLL_OCTAVES)) coct++;
471 break;
472
473 case GLUT_KEY_PAGE_DOWN:
474 break;
475
476 case GLUT_KEY_PAGE_UP:
477 break;
478 }
479 }
480
481
482
483
484
pattern_keyboardup(unsigned char key,int x,int y)485 void pattern_keyboardup(unsigned char key, int x, int y)
486 {
487 int i;
488
489 for (i=0;i<29;i++) {
490 if (pianokeys[i]==key) {
491 // if this key is still down, drop gate and trig
492 if (kpkeydown==(i+coct*12)) {
493 gate[0]=0;
494 kpkeydown=-1;
495 }
496 }
497 }
498 }
499
500
501
502
503
pattern_draw(void)504 void pattern_draw(void)
505 {
506 int i,j,n,l;
507 char tmps[256];
508 long ticks;
509 int rkdown;
510 float lineheight;
511
512 // draw keyboard, highlight a note if cursor is on piano roll
513 rkdown=-1;
514 if (piano_drag>=0) rkdown=piano_drag;
515 if (kpkeydown>=0) rkdown=kpkeydown;
516 for(i=0;i<PIANOROLL_OCTAVES;i++)
517 if ((coct+i)<10)draw_kboct(round(PIANOROLL_Y-(12*PIANOROLL_CELLHEIGHT*i)), 40, 12, coct+i, piano_note, rkdown);
518
519 // backgrounds and divider lines for note grid
520 for(j=0;j<(PIANOROLL_OCTAVES*12+1);j++) {
521 if (coct*12+j > MAX_NOTE) continue;
522
523 if (keycolor[j%12]) {
524 glColor4ub(0x20, 0x20, 0x20, 0xff);
525 } else {
526 glColor4ub(0x00, 0x00, 0x00, 0xff);
527 }
528 glBegin(GL_QUADS);
529 glVertex2f(PIANOROLL_X, PIANOROLL_Y-(j*PIANOROLL_CELLHEIGHT));
530 glVertex2f(PIANOROLL_X+(pattlen[cpatt]*(beats_per_measure*beatdiv)-piano_start)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(j*PIANOROLL_CELLHEIGHT));
531 glVertex2f(PIANOROLL_X+(pattlen[cpatt]*(beats_per_measure*beatdiv)-piano_start)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-((j+1)*PIANOROLL_CELLHEIGHT));
532 glVertex2f(PIANOROLL_X, PIANOROLL_Y-((j+1)*PIANOROLL_CELLHEIGHT));
533 glEnd();
534
535 glColor4f(0.3, 0.3, 0.3, 0.8);
536 if ((j%12)==0) glColor4f(0.5, 0.5, 0.5, 0.8);
537 glEnable(GL_LINE_STIPPLE);
538 glLineStipple(1, 0xaaaa);
539 glBegin(GL_LINES);
540 glVertex2f(PIANOROLL_X, PIANOROLL_Y-(j*PIANOROLL_CELLHEIGHT));
541 glVertex2f(PIANOROLL_X+(pattlen[cpatt]*(beats_per_measure*beatdiv)-piano_start)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(j*PIANOROLL_CELLHEIGHT));
542 glEnd();
543 glDisable(GL_LINE_STIPPLE);
544 }
545
546 // draw markers for beats and measures
547 if (((coct+PIANOROLL_OCTAVES)*12)<MAX_NOTE) {
548 lineheight=PIANOROLL_OCTAVES*12;
549 } else {
550 lineheight=PIANOROLL_OCTAVES*12 - (((coct+PIANOROLL_OCTAVES)*12)-MAX_NOTE);
551 }
552 lineheight*=PIANOROLL_CELLHEIGHT;
553 for(i=0,j=piano_start;j<(pattlen[cpatt]*(beats_per_measure*beatdiv)+1);i++,j++) {
554 glColor4f(0.2, 0.2, 0.2, 0.8);
555 if ((j%beatdiv)==0) {
556 // beat
557 glColor4f(0.5, 0.5, 0.5, 0.8);
558 }
559 int b=beats_per_measure*beatdiv;
560 if ((j%b)==0 && j<(pattlen[cpatt]*b)) {
561 // measure
562 sprintf(tmps, "%1d", (j/(beats_per_measure*beatdiv)));
563 render_text(tmps, 2.5+PIANOROLL_X+(i*PIANOROLL_CELLWIDTH), round(PIANOROLL_Y+PIANOROLL_CELLHEIGHT), 2, 0xffa0a0a0, 0);
564 glColor4f(0.7, 0.7, 0.7, 0.8);
565 }
566 glEnable(GL_LINE_STIPPLE);
567 glLineStipple(1, 0xaaaa);
568 glBegin(GL_LINES);
569 glVertex2f(PIANOROLL_X+i*PIANOROLL_CELLWIDTH, round(PIANOROLL_Y+PIANOROLL_CELLHEIGHT));
570 glVertex2f(PIANOROLL_X+i*PIANOROLL_CELLWIDTH, round(PIANOROLL_Y-lineheight)); //(PIANOROLL_OCTAVES*12)*PIANOROLL_CELLHEIGHT)));
571 glEnd();
572 glDisable(GL_LINE_STIPPLE);
573 }
574
575 // draw the playing position if pattern is playing
576 if (audiomode==AUDIOMODE_PATTERNPLAY) {
577 ticks=playpos / (OUTPUTFREQ/(bpm*256/60)); // calc tick from sample index
578 if ( (ticks>>6)>=piano_start && (ticks>>6)<(pattlen[cpatt]*(beats_per_measure*beatdiv)) ) // TODO: test if pos > pianostart+rollwidth
579 {
580 // within bounds
581 i=(ticks>>6)-piano_start;
582 glColor4f(1.0, 1.0, 1.0, 1.0);
583 glBegin(GL_LINES);
584 glVertex2f(PIANOROLL_X+i*PIANOROLL_CELLWIDTH, round(PIANOROLL_Y+PIANOROLL_CELLHEIGHT));
585 glVertex2f(PIANOROLL_X+i*PIANOROLL_CELLWIDTH, round(PIANOROLL_Y-((PIANOROLL_OCTAVES*12)*PIANOROLL_CELLHEIGHT)));
586 glEnd();
587 }
588 }
589
590 // draw the notes to the piano roll
591 for(i=0,j=piano_start;j<(pattlen[cpatt]*(beats_per_measure*beatdiv));i++,j++) {
592 n=pattdata[cpatt][j] & 0xff; // take only the low byte
593 l=(pattdata[cpatt][j]&NOTE_LEGATO) ? 0 : 1; // if legato, extend note for a pixel left
594 if (n>0) { // todo: fill with -1 instead as 0 is a valid midi note
595 if ( n>=(coct*12) && n<((coct+PIANOROLL_OCTAVES)*12)) {
596 // on the visible three octaves - plot it
597 glColor4ub(0xb0, 0x55, 0x00, 0xff);
598 glBegin(GL_QUADS);
599 glVertex2f(l+PIANOROLL_X+i*PIANOROLL_CELLWIDTH, PIANOROLL_Y-1-(n-coct*12)*PIANOROLL_CELLHEIGHT);
600 glVertex2f(PIANOROLL_X+(i+1)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-1-(n-coct*12)*PIANOROLL_CELLHEIGHT);
601 glVertex2f(PIANOROLL_X+(i+1)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(n-coct*12+1)*PIANOROLL_CELLHEIGHT);
602 glVertex2f(l+PIANOROLL_X+i*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(n-coct*12+1)*PIANOROLL_CELLHEIGHT);
603 glEnd();
604 if (pattdata[cpatt][j]&NOTE_ACCENT) {
605 // draw the accent mark
606 render_text(">",
607 l+PIANOROLL_X+i*PIANOROLL_CELLWIDTH+1,
608 PIANOROLL_Y-2-(n-coct*12+1)*PIANOROLL_CELLHEIGHT,
609 2, 0x000000ff, 0);
610 render_text(">",
611 l+PIANOROLL_X+i*PIANOROLL_CELLWIDTH,
612 PIANOROLL_Y-2-(n-coct*12+1)*PIANOROLL_CELLHEIGHT,
613 2, 0xffffffff, 0);
614 }
615 }
616 }
617 }
618
619 // draw the note highlighted by cursor
620 if (piano_hover >= 0) {
621 n=pattdata[cpatt][piano_start+piano_hover]&0xff;
622 if (n>0 && piano_note==n) {
623 j=piano_hover;
624 if (pattdata[cpatt][piano_start+j]&NOTE_LEGATO) {
625 // search backwards to find start of note
626 while (pattdata[cpatt][piano_start+j]&NOTE_LEGATO) j--;
627 }
628 for(l=1; pattdata[cpatt][piano_start+j+l]&NOTE_LEGATO; l++);
629
630 glColor4f(0.8, 0.8, 0.8, 0.8);
631 glEnable(GL_LINE_STIPPLE);
632 glLineStipple(1, 0x3333);
633 glBegin(GL_LINE_LOOP);
634 glVertex2f(1+PIANOROLL_X+j*PIANOROLL_CELLWIDTH, PIANOROLL_Y-1-(n-coct*12)*PIANOROLL_CELLHEIGHT);
635 glVertex2f(PIANOROLL_X+(j+l)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-1-(n-coct*12)*PIANOROLL_CELLHEIGHT);
636 glVertex2f(PIANOROLL_X+(j+l)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(n-coct*12+1)*PIANOROLL_CELLHEIGHT);
637 glVertex2f(1+PIANOROLL_X+j*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(n-coct*12+1)*PIANOROLL_CELLHEIGHT);
638 glEnd();
639 glDisable(GL_LINE_STIPPLE);
640 }
641 }
642
643 // drag an existing note up or down
644 if (piano_porta_drag >= 0) {
645 n=pattdata[cpatt][piano_start+piano_porta_drag];
646 glColor4ub(0xc0, 0xc0, 0xc0, 0x6f);
647 glBegin(GL_QUADS);
648 glVertex2f(1+PIANOROLL_X+piano_porta_drag*PIANOROLL_CELLWIDTH, PIANOROLL_Y-1-(n-coct*12)*PIANOROLL_CELLHEIGHT);
649 glVertex2f(PIANOROLL_X+(piano_porta_drag+piano_porta_drag_len)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-1-(n-coct*12)*PIANOROLL_CELLHEIGHT);
650 glVertex2f(PIANOROLL_X+(piano_porta_drag+piano_porta_drag_len)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(n-coct*12+1)*PIANOROLL_CELLHEIGHT);
651 glVertex2f(1+PIANOROLL_X+piano_porta_drag*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(n-coct*12+1)*PIANOROLL_CELLHEIGHT);
652 glEnd();
653 if (piano_porta_drag_from != pattdata[cpatt][piano_start+piano_porta_drag]) {
654 n=piano_porta_drag_from;
655 glColor4ub(0x90, 0x90, 0x90, 0x6f);
656 glBegin(GL_QUADS);
657 glVertex2f(1+PIANOROLL_X+piano_porta_drag*PIANOROLL_CELLWIDTH, PIANOROLL_Y-1-(n-coct*12)*PIANOROLL_CELLHEIGHT);
658 glVertex2f(PIANOROLL_X+(piano_porta_drag+piano_porta_drag_len)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-1-(n-coct*12)*PIANOROLL_CELLHEIGHT);
659 glVertex2f(PIANOROLL_X+(piano_porta_drag+piano_porta_drag_len)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(n-coct*12+1)*PIANOROLL_CELLHEIGHT);
660 glVertex2f(1+PIANOROLL_X+piano_porta_drag*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(n-coct*12+1)*PIANOROLL_CELLHEIGHT);
661 glEnd();
662 }
663 }
664
665 // draw the new note being dragged onto piano roll
666 if (piano_drag >= 0) {
667 n=pattdata[cpatt][piano_start+piano_drag];
668 glColor4ub(0xc0, 0xc0, 0xc0, 0x6f);
669 glBegin(GL_QUADS);
670 glVertex2f(1+PIANOROLL_X+piano_drag*PIANOROLL_CELLWIDTH, PIANOROLL_Y-1-(n-coct*12)*PIANOROLL_CELLHEIGHT);
671 glVertex2f(PIANOROLL_X+(piano_dragto+1)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-1-(n-coct*12)*PIANOROLL_CELLHEIGHT);
672 glVertex2f(PIANOROLL_X+(piano_dragto+1)*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(n-coct*12+1)*PIANOROLL_CELLHEIGHT);
673 glVertex2f(1+PIANOROLL_X+piano_drag*PIANOROLL_CELLWIDTH, PIANOROLL_Y-(n-coct*12+1)*PIANOROLL_CELLHEIGHT);
674 glEnd();
675 }
676
677 // highlight the note row if a key is currently down
678 if (kpkeydown >=0 && kpkeydown>=(coct*12) && kpkeydown<((coct+PIANOROLL_OCTAVES)*12)) {
679 glColor4ub(0xc0, 0xc0, 0xc0, 0x3f);
680 glBegin(GL_QUADS);
681
682 glVertex2f(PIANOROLL_X,
683 PIANOROLL_Y-1-(kpkeydown-coct*12)*PIANOROLL_CELLHEIGHT);
684
685 glVertex2f(PIANOROLL_X+(pattlen[cpatt]*(beats_per_measure*beatdiv)-piano_start)*PIANOROLL_CELLWIDTH,
686 PIANOROLL_Y-1-(kpkeydown-coct*12)*PIANOROLL_CELLHEIGHT);
687
688 glVertex2f(PIANOROLL_X+(pattlen[cpatt]*(beats_per_measure*beatdiv)-piano_start)*PIANOROLL_CELLWIDTH,
689 PIANOROLL_Y-(kpkeydown-coct*12+1)*PIANOROLL_CELLHEIGHT);
690
691 glVertex2f(PIANOROLL_X,
692 PIANOROLL_Y-(kpkeydown-coct*12+1)*PIANOROLL_CELLHEIGHT);
693
694 glEnd();
695 }
696
697 // draw the horizontal slider
698 draw_hslider(PIANOROLL_X, PIANOROLL_Y+12, (DS_WIDTH-(PIANOROLL_X+6)), 12,
699 piano_start, (DS_WIDTH-(PIANOROLL_X+4))/PIANOROLL_CELLWIDTH,
700 pattlen[cpatt]*(beats_per_measure*beatdiv),
701 slide_hover);
702
703 // draw the ui elements on the pattern page
704 draw_button(14, DS_HEIGHT-14, 16, "<<", patt_ui[B_PREV]);
705 sprintf(tmps, "%02d", cpatt);
706 draw_textbox(39, DS_HEIGHT-14, 16, 24, tmps, 0);
707 draw_button(64, DS_HEIGHT-14, 16, ">>", patt_ui[B_NEXT]);
708
709 draw_button(98, DS_HEIGHT-14, 16, "<<", patt_ui[B_SHORTER]);
710 sprintf(tmps, "%1d meas", (unsigned int)(pattlen[cpatt]));
711 draw_textbox(139, DS_HEIGHT-14, 16, 56, tmps, 0);
712 draw_button(180, DS_HEIGHT-14, 16, ">>", patt_ui[B_LONGER]);
713
714 draw_button(12, PIANOROLL_Y+15.5, 16, "DN", patt_ui[B_OCTDN]);
715 draw_button(32, PIANOROLL_Y+15.5, 16, "UP", patt_ui[B_OCTUP]);
716
717 draw_button(214, DS_HEIGHT-14, 16, "<<", patt_ui[B_PREVSYN]);
718 sprintf(tmps, "%02d:%s", csynth[cpatch], patchname[csynth][csynth[cpatch]]);
719 draw_textbox(322, DS_HEIGHT-14, 16, 188, tmps, patt_ui[B_SYNNAME]);
720 draw_button(430, DS_HEIGHT-14, 16, ">>", patt_ui[B_NEXTSYN]);
721
722 ticks=playpos / (OUTPUTFREQ/(bpm*256/60));
723 if (audiomode==AUDIOMODE_PATTERNPLAY) {
724 sprintf(tmps, "%02ld:%02ld", (ticks>>10), ((ticks>>6)&15));
725 } else {
726 sprintf(tmps, "play");
727 }
728 draw_textbox(482, DS_HEIGHT-14, 16, 42, tmps, patt_ui[B_PATTPLAY]);
729
730 draw_button(555, DS_HEIGHT-14, 16, "X", patt_ui[B_PATTCLEAR]);
731
732 draw_button(622, DS_HEIGHT-14, 16, "C", patt_ui[B_COPY]);
733 draw_button(644, DS_HEIGHT-14, 16, "V", patt_ui[B_PASTE]);
734 if (patt_clipboard_len==0) {
735 glColor4f(0,0,0,0.4f);
736 glBegin(GL_QUADS);
737 glVertex2f(644-8, (DS_HEIGHT-14)-8); glVertex2f(644+8, (DS_HEIGHT-14)-8);
738 glVertex2f(644+8, (DS_HEIGHT-14)+8); glVertex2f(644-8, (DS_HEIGHT-14)+8);
739 glEnd();
740 }
741
742 }
743