Unhardcoded ticks per beat. Fixed triplet bugs.
[epichord.git] / src / eventedit.cpp
blobb1f6ce067c4720adb3f5c38a745a481bb12a2a97
1 /*
2 Epichord - a midi sequencer
3 Copyright (C) 2008 Evan Rinehart
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to
18 The Free Software Foundation, Inc.
19 51 Franklin Street, Fifth Floor
20 Boston, MA 02110-1301, USA
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <vector>
27 #include <fltk/Group.h>
28 #include <fltk/Widget.h>
29 #include <fltk/events.h>
31 #include "ui.h"
33 #include "eventedit.h"
35 #include "uihelper.h"
37 #include "backend.h"
39 #define MAG_MAX 16383
41 #define SWAP(X,Y) {int tmp=X; X=Y; Y=tmp;}
43 extern struct conf config;
45 extern UI* ui;
46 extern std::vector<track*> tracks;
48 extern char controller_names[128][64];
50 using namespace fltk;
52 EventEdit::EventEdit(int x, int y, int w, int h, const char* label = 0) : fltk::Widget(x, y, w, h, label) {
54 zoom = 15;
56 event_type = MIDI_NOTE_ON;
57 controller_type = 0;
60 label_flag = 0;
61 select_flag = 0;
62 insert_flag = 0;
63 line_flag = 0;
64 paste_flag = 0;
65 delete_flag = 0;
66 box_flag = 0;
68 for(int i=0; i<134; i++){
69 has[i] = 0;
72 line_t1 = 0;
73 line_t2 = 0;
75 cur_seqpat = NULL;
78 int EventEdit::handle(int event){
79 int X,Y;
80 switch(event){
81 case FOCUS:
82 return 1;
83 case MOUSEWHEEL:
84 if(event_dy() < 0){
85 event_type_next();
87 else if(event_dy() > 0){
88 event_type_prev();
90 redraw();
91 return 1;
92 break;
93 case PUSH:
94 X=event_x();
95 Y=event_y();
96 if(event_button()==1){
97 if(event_state()&fltk::CTRL){//insert
98 insert_flag = 1;
99 insert_x = X;
100 insert_y = Y;
101 insert_t = quantize(xpix2tick(X));
102 insert_M = ypix2mag(Y);
104 else if(event_state()&fltk::SHIFT){//box select
105 box_flag=1;
106 box_x1=X;
107 box_x2=X;
108 box_y1=Y;
109 box_y2=Y;
110 box_t1=xpix2tick(X);
111 box_t2=box_t1;
112 box_m1=ypix2mag(Y);
113 box_m2=box_m1;
115 else{//line
116 line_flag=1;
117 line_x1=X;
118 line_x2=X;
119 line_y1=Y;
120 line_y2=Y;
121 line_t1=xpix2tick(X);
122 line_M1=ypix2mag(Y);
123 line_t2=line_t1;
124 line_M2=line_M1;
127 else if(event_button()==2){//paste
128 paste_flag = 1;
129 paste_x = X;
130 paste_t = xpix2tick(X);
132 else if(event_button()==3){//delete
133 delete_flag = 1;
134 delete_x1 = X;
135 delete_x2 = X;
136 delete_t1 = xpix2tick(X);
137 delete_t2 = delete_t1;
139 redraw();
140 return 1;
141 break;
142 case DRAG:
143 X=event_x();
144 Y=event_y();
145 if(line_flag){
146 line_x2 = X;
147 line_y2 = Y;
148 line_t2 = xpix2tick(X);
149 line_M2 = ypix2mag(Y);
151 if(box_flag){
152 box_x2 = X;
153 box_y2 = Y;
154 box_t2 = xpix2tick(X);
155 box_m2 = ypix2mag(Y);
157 if(insert_flag){
158 insert_x = X;
159 insert_y = Y;
160 insert_t = quantize(xpix2tick(X));
161 insert_M = ypix2mag(Y);
163 if(delete_flag){
164 delete_x2 = X;
165 delete_t2 = xpix2tick(X);
167 if(paste_flag){
168 paste_x = X;
169 paste_t = xpix2tick(X);
171 redraw();
172 break;
173 case RELEASE:
174 if(event_button()==1){//insert, box, line
175 if(line_flag){
176 apply_line();
177 line_flag=0;
179 if(box_flag){
180 apply_box();
181 ui->piano_roll->redraw();
182 box_flag=0;
184 if(insert_flag){
185 apply_insert();
186 insert_flag=0;
189 else if(event_button()==2){//complete paste
190 apply_paste();
191 paste_flag=0;
193 else if(event_button()==3){//delete
194 apply_delete();
195 delete_flag = 0;
196 clear_selection();
198 redraw();
199 break;
201 return 0;
204 void EventEdit::draw(){
205 fltk::setfont(fltk::HELVETICA,12);
206 fltk::setcolor(fltk::GRAY05);
207 fltk::fillrect(0,0,w(),h());
209 fltk::push_clip(0,0,w(),h());
211 if(delete_flag){
212 fltk::setcolor(fltk::color(64,0,0));
213 int X1,X2;
214 if(delete_x1>delete_x2){
215 X1=delete_x2;
216 X2=delete_x1;
218 else{
219 X1=delete_x1;
220 X2=delete_x2;
222 fltk::fillrect(X1,0,X2-X1,h());
225 fltk::setcolor(fltk::GRAY20);
226 fltk::drawtext(event_type_name(), 2, h()-5);
228 fltk::setcolor(fltk::GRAY20);
229 fltk::fillrect(0,h()-3,w(),1);
230 for(int i=zoom - scroll; i<w(); i+=zoom){
231 if(i>=0){
232 fltk::drawline(i,0,i,h()-1);
236 fltk::setcolor(fltk::GRAY50);
237 for(int i=zoom*4-scroll; i<w(); i+=zoom*4){
238 if(i>=0){
239 fltk::drawline(i,0,i,h()-1);
243 fltk::setcolor(fltk::WHITE);
244 int M = config.beats_per_measure;
245 int I = 0;
246 for(int i=1; I<w(); i++){
247 I = i*zoom*4*M - scroll;
248 if(I>=0){
249 fltk::fillrect(I,0,1,h());
253 fltk::setcolor(fltk::color(128,0,0));
254 int rightend = tick2xpix(cur_seqpat->dur)-scroll;
255 if(rightend >=0 && rightend < w()){
256 fltk::fillrect(rightend,0,1,h());
259 mevent* e = cur_seqpat->p->events->next;
261 while(e){
262 if(e->type==event_type){
263 if(e->type==MIDI_CONTROLLER_CHANGE){
264 if(e->value1 == controller_type){
265 M = val2mag(e->value2);
267 else{
268 e=e->next;
269 continue;
272 else{
273 switch(e->type){
274 case -1:
275 e = e->next;
276 continue;
277 case MIDI_PROGRAM_CHANGE:
278 case MIDI_CHANNEL_PRESSURE:
279 M = val2mag(e->value1);
280 break;
281 default:
282 M = val2mag(e->value2);
283 break;
286 int T1 = line_t1;
287 int T2 = line_t2;
288 int M1 = line_M1;
289 int M2 = line_M2;
290 if(T1>T2){
291 int tmp = T2;
292 T2 = T1;
293 T1 = tmp;
294 tmp = M2;
295 M2 = M1;
296 M1 = tmp;
298 if(line_flag && e->tick > T1 && e->tick < T2){
299 if(!select_flag || e->selected){
300 float m = (float)(M2-M1)/(T2-T1);
301 float b = M1 - T1*m;
302 M = (int)(m*e->tick + b);
303 if(M<0){M=0;}
304 if(M>MAG_MAX){M=MAG_MAX;}
307 int X = tick2xpix(e->tick) - scroll;
308 int Y = mag2ypix(M);
309 int H = h()-Y;
310 if(!(X<0 || X>w())){
311 fltk::Color c1,c2,c3;
312 c1 = fltk::BLACK;
313 c2 = fltk::BLACK;
314 c3 = fltk::BLACK;
315 get_event_color(e,&c1,&c2,&c3);
317 fltk::setcolor(c1);
318 fltk::fillrect(X,Y+1,1,H);
319 fltk::fillrect(X+1,Y,1,1);
320 fltk::setcolor(c2);
321 fltk::fillrect(X+1,Y+1,1,H);
322 fltk::setcolor(c3);
323 fltk::fillrect(X,Y,1,1);
324 if(label_flag){
325 fltk::setcolor(c1);
326 char buf[16];
327 if(e->type == MIDI_PITCH_WHEEL){
328 snprintf(buf,16,"%d",M);
330 else{
331 snprintf(buf,16,"%d",mag2val(M));
333 fltk::drawtext(buf,X+2,Y+12<h()-3?Y+12:h()-3);
337 e = e->next;
340 if(line_flag){
341 fltk::setcolor(fltk::BLUE);
342 fltk::drawline(line_x1,line_y1,line_x2,line_y2);
345 if(box_flag){
346 fltk::setcolor(fltk::GREEN);
347 int X1,X2,Y1,Y2;
348 if(box_x1>box_x2){
349 X1=box_x2;
350 X2=box_x1;
352 else{
353 X1=box_x1;
354 X2=box_x2;
356 if(box_y1>box_y2){
357 Y1=box_y2;
358 Y2=box_y1;
360 else{
361 Y1=box_y1;
362 Y2=box_y2;
364 fltk::fillrect(X1,Y1,X2-X1,1);
365 fltk::fillrect(X1,Y1,1,Y2-Y1);
366 fltk::fillrect(X2,Y1,1,Y2-Y1);
367 fltk::fillrect(X1,Y2,X2-X1,1);
370 if(insert_flag){
371 fltk::setcolor(fltk::CYAN);
372 int X = tick2xpix(insert_t)-scroll;
373 int Y = insert_y;
374 if(Y<0){Y=0;}
375 if(Y>h()-3){Y=h()-3;}
376 int M = insert_M;
377 if(M<0){M=0;}
378 if(M>MAG_MAX){M=MAG_MAX;}
379 fltk::fillrect(X,Y,2,h()-Y);
380 if(label_flag){
381 char buf[16];
382 if(event_type == MIDI_PITCH_WHEEL){
383 snprintf(buf,16,"%d",M);
385 else{
386 snprintf(buf,16,"%d",mag2val(M));
388 fltk::drawtext(buf,X-fltk::getwidth(buf),Y+12<h()-3?Y+12:h()-3);
393 fltk::pop_clip();
398 void EventEdit::load(seqpat* s){
399 cur_seqpat = s;
400 cur_track = tracks[s->track];
401 recount_has();
404 int EventEdit::tick2xpix(int tick){
405 return tick*zoom*4 / TICKS_PER_BEAT;
408 const char* EventEdit::event_type_name(){
409 switch(event_type){
410 case MIDI_NOTE_OFF:
411 return "note off velocity";
412 case MIDI_NOTE_ON:
413 return "note on velocity";
414 case MIDI_AFTERTOUCH:
415 return "polyphonic key pressure (aftertouch)";
416 case MIDI_CONTROLLER_CHANGE:
417 return controller_names[controller_type];
418 case MIDI_PROGRAM_CHANGE:
419 return "program change";
420 case MIDI_CHANNEL_PRESSURE:
421 return "channel pressure";
422 case MIDI_PITCH_WHEEL:
423 return "pitch wheel";
424 default:
425 return "booya";
429 void EventEdit::event_type_next(){
430 switch(event_type){
431 case 0x80:
432 event_type = 0xA0;
433 break;
434 case 0x90:
435 event_type = 0x80;
436 break;
437 case 0xA0:
438 event_type = 0xC0;
439 break;
440 case 0xB0:
441 if(controller_type == 127){
442 event_type = 0x90;
444 else{
445 controller_type++;
447 break;
448 case 0xE0:
449 event_type = 0xB0;
450 controller_type = 0;
451 break;
452 default:
453 event_type += 0x10;
457 void EventEdit::event_type_prev(){
458 switch(event_type){
459 case 0x80:
460 event_type = 0x90;
461 break;
462 case 0x90:
463 event_type = 0xB0;
464 controller_type = 127;
465 break;
466 case 0xC0:
467 event_type = 0xA0;
468 break;
469 case 0xB0:
470 if(controller_type == 0){
471 event_type = 0xE0;
473 else{
474 controller_type--;
476 break;
477 case 0xA0:
478 event_type = 0x80;
479 break;
480 default:
481 event_type -= 0x10;
485 void EventEdit::set_event_type(int type, int controller){
486 event_type = type;
487 controller_type = controller;
491 int EventEdit::ypix2mag(int ypix){
492 int H = h()-3;
493 int R = ypix*MAG_MAX/H;
494 return MAG_MAX-R;
497 int EventEdit::mag2ypix(int mag){
498 int H = mag*(h()-3)/MAG_MAX;
499 return h()-H-3;
502 int EventEdit::mag2val(int mag){
503 return mag*127/MAG_MAX;
506 int EventEdit::val2mag(int val){
507 return val*MAG_MAX/127;
510 void EventEdit::apply_line(){
511 mevent* e = cur_seqpat->p->events;
512 Command* c;
513 int N = 0;
514 int T1 = line_t1;
515 int T2 = line_t2;
516 int M1 = line_M1;
517 int M2 = line_M2;
518 int tmp;
519 if(T1>T2){SWAP(T1,T2);}
520 //if(M1>M2){SWAP(M1,M2);}
522 while(e->tick < T1){
523 e = e->next;
524 if(!e){
525 return;
528 while(e){
529 if(e->tick > T2){
530 break;
532 if(match_event_type(e) && (e->selected || !select_flag)){
533 float m = (float)(M2-M1)/(line_t2-line_t1);
534 float b = M1 - m*line_t1;
535 int M = (int)(m*e->tick + b);
536 int V1, V2;
537 if(M<0){M=0;}
538 if(M>MAG_MAX){M=MAG_MAX;}
539 switch(e->type){
540 case MIDI_NOTE_OFF:
541 case MIDI_NOTE_ON:
542 case MIDI_AFTERTOUCH:
543 case MIDI_CONTROLLER_CHANGE:
544 V1 = e->value1;
545 V2 = mag2val(M);
546 break;
547 case MIDI_PROGRAM_CHANGE:
548 case MIDI_CHANNEL_PRESSURE:
549 V1 = mag2val(M);
550 break;
551 case MIDI_PITCH_WHEEL:
552 V1 = M&0x7f;
553 V2 = (M&0x3f80) >> 7;
554 break;
556 c = new ChangeEvent(e,V1,V2);
557 set_undo(c);
558 N++;
560 e = e->next;
562 undo_push(N);
565 void EventEdit::apply_box(){
566 select_flag=1;
567 mevent* e = cur_seqpat->p->events->next;
568 int T1=box_t1;
569 int T2=box_t2;
570 int M1 = box_m1;
571 int M2 = box_m2;
572 int tmp;
573 while(e){
574 if(e->tick > T2){break;}
575 int M = get_event_mag(e);
576 if(T1>T2){SWAP(T1,T2);}
577 if(M1<M2){SWAP(M1,M2);}
578 if(e->tick > T1 && e->tick < T2 && M > M2){
579 e->selected = 1;
581 e=e->next;
585 void EventEdit::apply_insert(){
586 int V1,V2;
587 Command* c;
588 get_event_value(&V1,&V2);
589 c = new CreateEvent(cur_seqpat->p,event_type,insert_t,V1,V2);
590 set_undo(c);
591 undo_push(1);
593 switch(event_type){
594 case MIDI_NOTE_ON: has[0]=1; break;
595 case MIDI_NOTE_OFF: has[1]=1; break;
596 case MIDI_AFTERTOUCH: has[2]=1; break;
597 case MIDI_PROGRAM_CHANGE: has[3]=1; break;
598 case MIDI_CHANNEL_PRESSURE: has[4]=1; break;
599 case MIDI_PITCH_WHEEL: has[5]=1; break;
600 default: has[controller_type+6]=1; break;
603 if(event_type==MIDI_NOTE_ON){
604 ui->piano_roll->redraw();
608 void EventEdit::delete_events(int (EventEdit::*pred)(mevent* e)){
609 mevent* e = cur_seqpat->p->events->next;
610 mevent* next;
611 Command* c;
612 int N=0;
613 while(e){
614 if(((this)->*(pred))(e)){
615 next = e->next;
616 c = new DeleteEvent(e);
617 set_undo(c);
618 N++;
619 e = next;
621 else{
622 e = e->next;
625 undo_push(N);
629 int EventEdit::delete_type_in_range_pred(mevent* e){
630 int L = delete_t1;
631 int R = delete_t2;
632 int tmp;
633 if(L>R){SWAP(L,R);}
634 if(e->tick > L && e->tick < R && match_event_type(e))
635 return 1;
636 else
637 return 0;
640 void EventEdit::apply_delete(){
641 delete_events(&EventEdit::delete_type_in_range_pred);
642 ui->piano_roll->redraw();
647 void EventEdit::apply_paste(){
648 printf("apply paste\n");
651 int EventEdit::match_event_type(mevent* e){
652 if(e->type == event_type){
653 if(e->type == MIDI_CONTROLLER_CHANGE){
654 if(e->value1 == controller_type){
655 return 1;
658 else{
659 return 1;
662 return 0;
665 void EventEdit::get_event_color(mevent* e, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3){
667 int T1,T2;
668 int tmp;
669 if(delete_flag){
670 T1=delete_t1;
671 T2=delete_t2;
672 if(T1>T2){SWAP(T1,T2);}
673 if(e->tick > T1 && e->tick < T2){
674 *c1 = fltk::color(229,79,75);
675 *c2 = fltk::color(120,60,58);
676 *c3 = fltk::color(225,131,109);
677 return;
681 if(box_flag){
682 T1=box_t1;
683 T2=box_t2;
684 int M1 = box_m1;
685 int M2 = box_m2;
686 int M = get_event_mag(e);
687 if(T1>T2){SWAP(T1,T2);}
688 if(M1<M2){SWAP(M1,M2);}
689 if(e->tick > T1 && e->tick < T2 && M > M2){
690 *c1 = fltk::color(108,229,75);
691 *c2 = fltk::color(71,120,59);
692 *c3 = fltk::color(108,229,75);
693 return;
697 if(line_flag){
698 T1=line_t1;
699 T2=line_t2;
700 if(T1>T2){SWAP(T1,T2);}
701 if(e->tick > T1 && e->tick < T2){
702 if(!select_flag || e->selected){
703 *c1 = fltk::color(75,119,229);
704 *c2 = fltk::color(58,76,120);
705 *c3 = fltk::color(109,123,225);
706 return;
711 if(e->selected){
712 *c1 = fltk::color(255,248,47);
713 *c2 = fltk::color(140,137,46);
714 *c3 = fltk::color(232,255,37);
715 return;
718 *c1 = fltk::color(169,75,229);
719 *c2 = fltk::color(95,58,119);
720 *c3 = fltk::color(198,109,225);
723 void EventEdit::get_event_value(int* v1, int* v2){
724 int M = insert_M;
725 if(M<0){M=0;}
726 if(M>MAG_MAX){M=MAG_MAX;}
727 switch(event_type){
728 case MIDI_NOTE_OFF:
729 case MIDI_NOTE_ON:
730 case MIDI_AFTERTOUCH:
731 *v1 = 60;
732 *v2 = mag2val(M);
733 break;
734 case MIDI_CONTROLLER_CHANGE:
735 *v1 = controller_type;
736 *v2 = mag2val(M);
737 break;
738 case MIDI_PROGRAM_CHANGE:
739 case MIDI_CHANNEL_PRESSURE:
740 *v1 = mag2val(M);
741 break;
742 case MIDI_PITCH_WHEEL:
743 *v1 = M&0x7f;
744 *v2 = (M&0x3f80) >> 7;
745 break;
749 int EventEdit::xpix2tick(int xpix){
750 ui->piano_roll->xpix2tick(xpix+scroll);
753 int EventEdit::get_event_mag(mevent* e){
754 switch(e->type){
755 case MIDI_NOTE_OFF:
756 case MIDI_NOTE_ON:
757 case MIDI_AFTERTOUCH:
758 case MIDI_CONTROLLER_CHANGE:
759 return val2mag(e->value2);
760 case MIDI_PROGRAM_CHANGE:
761 case MIDI_CHANNEL_PRESSURE:
762 return val2mag(e->value1);
763 case MIDI_PITCH_WHEEL:
764 return e->value1 | (e->value2<<7);
766 return 0;
770 int EventEdit::delete_type_all_pred(mevent* e){
771 if(match_event_type(e))
772 return 1;
773 else
774 return 0;
777 int EventEdit::delete_all_non_note_pred(mevent* e){
778 if(e->type != MIDI_NOTE_ON && e->type != MIDI_NOTE_OFF)
779 return 1;
780 else
781 return 0;
784 int EventEdit::delete_all_pred(mevent* e){
785 return 1;
788 void EventEdit::clear_events(){
789 delete_events(&EventEdit::delete_type_all_pred);
790 switch(event_type){
791 case MIDI_NOTE_ON: has[0]=0; break;
792 case MIDI_NOTE_OFF: has[1]=0; break;
793 case MIDI_AFTERTOUCH: has[2]=0; break;
794 case MIDI_PROGRAM_CHANGE: has[3]=0; break;
795 case MIDI_CHANNEL_PRESSURE: has[4]=0; break;
796 case MIDI_PITCH_WHEEL: has[5]=0; break;
797 default: has[controller_type+6]=0; break;
799 redraw();
800 ui->piano_roll->redraw();
801 ui->event_menu->redraw();
804 void EventEdit::clear_non_note_events(){
805 delete_events(&EventEdit::delete_all_non_note_pred);
806 redraw();
807 ui->piano_roll->redraw();
808 for(int i=2;i<134;i++){
809 has[i]=0;
811 ui->event_menu->redraw();
814 void EventEdit::clear_all_events(){
815 delete_events(&EventEdit::delete_all_pred);
816 redraw();
817 ui->piano_roll->redraw();
818 for(int i=0;i<134;i++){
819 has[i]=0;
821 ui->event_menu->redraw();
824 void EventEdit::clear_selected_events(){
828 void EventEdit::clear_selection(){
829 select_flag = 0;
830 mevent* e = cur_seqpat->p->events->next;
831 while(e){
832 e->selected=0;
833 e = e->next;
835 redraw();
836 ui->piano_roll->redraw();
839 int EventEdit::quantize(int tick){
840 return ui->piano_roll->quantize(tick);
843 void EventEdit::recount_has(){
844 for(int i=0; i<134; i++){has[i]=0;}
845 mevent* e = cur_seqpat->p->events->next;
846 while(e){
847 switch(e->type){
848 case MIDI_NOTE_ON: has[0]=1; break;
849 case MIDI_NOTE_OFF: has[1]=1; break;
850 case MIDI_AFTERTOUCH: has[2]=1; break;
851 case MIDI_PROGRAM_CHANGE: has[3]=1; break;
852 case MIDI_CHANNEL_PRESSURE: has[4]=1; break;
853 case MIDI_PITCH_WHEEL: has[5]=1; break;
854 default: has[e->value1+6]=1; break;
856 e = e->next;