Import now sets default tempo if no tempo events.
[epichord.git] / src / eventedit.cpp
blob13345f1276ab78c738405cdf874095319bd037ab
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"
38 #define MAG_MAX 16383
40 #define SWAP(X,Y) tmp=X; X=Y; Y=tmp;
42 extern struct conf config;
44 extern UI* ui;
45 extern std::vector<track*> tracks;
47 extern char controller_names[128][64];
49 using namespace fltk;
51 EventEdit::EventEdit(int x, int y, int w, int h, const char* label = 0) : fltk::Widget(x, y, w, h, label) {
53 zoom = 30;
55 event_type = MIDI_NOTE_ON;
56 controller_type = 0;
59 label_flag = 0;
60 select_flag = 0;
61 insert_flag = 0;
62 line_flag = 0;
63 paste_flag = 0;
64 delete_flag = 0;
65 box_flag = 0;
67 for(int i=0; i<134; i++){
68 has[i] = 0;
72 int EventEdit::handle(int event){
73 int X,Y;
74 switch(event){
75 case FOCUS:
76 return 1;
77 case MOUSEWHEEL:
78 if(event_dy() < 0){
79 event_type_next();
81 else if(event_dy() > 0){
82 event_type_prev();
84 redraw();
85 return 1;
86 break;
87 case PUSH:
88 X=event_x();
89 Y=event_y();
90 if(event_button()==1){
91 if(event_state()&fltk::CTRL){//insert
92 insert_flag = 1;
93 insert_x = X;
94 insert_y = Y;
95 insert_t = quantize(xpix2tick(X));
96 insert_M = ypix2mag(Y);
98 else if(event_state()&fltk::SHIFT){//box select
99 box_flag=1;
100 box_x1=X;
101 box_x2=X;
102 box_y1=Y;
103 box_y2=Y;
104 box_t1=xpix2tick(X);
105 box_t2=box_t1;
106 box_m1=ypix2mag(Y);
107 box_m2=box_m1;
109 else{//line
110 line_flag=1;
111 line_x1=X;
112 line_x2=X;
113 line_y1=Y;
114 line_y2=Y;
115 line_t1=xpix2tick(X);
116 line_M1=ypix2mag(Y);
117 line_t2=line_t1;
118 line_M2=line_M1;
121 else if(event_button()==2){//paste
122 paste_flag = 1;
123 paste_x = X;
124 paste_t = xpix2tick(X);
126 else if(event_button()==3){//delete
127 delete_flag = 1;
128 delete_x1 = X;
129 delete_x2 = X;
130 delete_t1 = xpix2tick(X);
131 delete_t2 = delete_t1;
133 redraw();
134 return 1;
135 break;
136 case DRAG:
137 X=event_x();
138 Y=event_y();
139 if(line_flag){
140 line_x2 = X;
141 line_y2 = Y;
142 line_t2 = xpix2tick(X);
143 line_M2 = ypix2mag(Y);
145 if(box_flag){
146 box_x2 = X;
147 box_y2 = Y;
148 box_t2 = xpix2tick(X);
149 box_m2 = ypix2mag(Y);
151 if(insert_flag){
152 insert_x = X;
153 insert_y = Y;
154 insert_t = quantize(xpix2tick(X));
155 insert_M = ypix2mag(Y);
157 if(delete_flag){
158 delete_x2 = X;
159 delete_t2 = xpix2tick(X);
161 if(paste_flag){
162 paste_x = X;
163 paste_t = xpix2tick(X);
165 redraw();
166 break;
167 case RELEASE:
168 if(event_button()==1){//insert, box, line
169 if(line_flag){
170 apply_line();
171 line_flag=0;
173 if(box_flag){
174 apply_box();
175 ui->piano_roll->redraw();
176 box_flag=0;
178 if(insert_flag){
179 apply_insert();
180 insert_flag=0;
183 else if(event_button()==2){//complete paste
184 apply_paste();
185 paste_flag=0;
187 else if(event_button()==3){//delete
188 apply_delete();
189 delete_flag = 0;
190 clear_selection();
192 redraw();
193 break;
195 return 0;
198 void EventEdit::draw(){
199 fltk::setfont(fltk::HELVETICA,12);
200 fltk::setcolor(fltk::GRAY05);
201 fltk::fillrect(0,0,w(),h());
203 fltk::push_clip(0,0,w(),h());
205 if(delete_flag){
206 fltk::setcolor(fltk::color(64,0,0));
207 int X1,X2;
208 if(delete_x1>delete_x2){
209 X1=delete_x2;
210 X2=delete_x1;
212 else{
213 X1=delete_x1;
214 X2=delete_x2;
216 fltk::fillrect(X1,0,X2-X1,h());
219 fltk::setcolor(fltk::GRAY20);
220 fltk::drawtext(event_type_name(), 2, h()-5);
222 fltk::setcolor(fltk::GRAY20);
223 fltk::fillrect(0,h()-3,w(),1);
224 for(int i=zoom - scroll; i<w(); i+=zoom){
225 fltk::drawline(i,0,i,h()-1);
228 fltk::setcolor(fltk::GRAY50);
229 for(int i=zoom*4-scroll; i<w(); i+=zoom*4){
230 fltk::drawline(i,0,i,h()-1);
233 fltk::setcolor(fltk::WHITE);
234 int M = config.beats_per_measure;
235 int I = 0;
236 for(int i=1; I<w(); i++){
237 I = i*zoom*4*M - scroll;
238 fltk::fillrect(I,0,1,h());
241 fltk::setcolor(fltk::color(128,0,0));
242 int rightend = tick2xpix(cur_seqpat->dur)-scroll;
243 fltk::fillrect(rightend,0,1,h());
245 mevent* e = cur_seqpat->p->events->next;
247 while(e){
248 if(e->type==event_type){
249 if(e->type==MIDI_CONTROLLER_CHANGE){
250 if(e->value1 == controller_type){
251 M = val2mag(e->value2);
253 else{
254 e=e->next;
255 continue;
258 else{
259 switch(e->type){
260 case -1:
261 e = e->next;
262 continue;
263 case MIDI_PROGRAM_CHANGE:
264 case MIDI_CHANNEL_PRESSURE:
265 M = val2mag(e->value1);
266 break;
267 default:
268 M = val2mag(e->value2);
269 break;
272 int T1 = line_t1;
273 int T2 = line_t2;
274 int M1 = line_M1;
275 int M2 = line_M2;
276 if(T1>T2){
277 int tmp = T2;
278 T2 = T1;
279 T1 = tmp;
280 tmp = M2;
281 M2 = M1;
282 M1 = tmp;
284 if(line_flag && e->tick > T1 && e->tick < T2){
285 if(!select_flag || e->selected){
286 float m = (float)(M2-M1)/(T2-T1);
287 float b = M1 - T1*m;
288 M = (int)(m*e->tick + b);
289 if(M<0){M=0;}
290 if(M>MAG_MAX){M=MAG_MAX;}
293 int X = tick2xpix(e->tick) - scroll;
294 int Y = mag2ypix(M);
295 int H = h()-Y;
297 fltk::Color c1,c2,c3;
298 c1 = fltk::BLACK;
299 c2 = fltk::BLACK;
300 c3 = fltk::BLACK;
301 get_event_color(e,&c1,&c2,&c3);
303 fltk::setcolor(c1);
304 fltk::fillrect(X,Y+1,1,H);
305 fltk::fillrect(X+1,Y,1,1);
306 fltk::setcolor(c2);
307 fltk::fillrect(X+1,Y+1,1,H);
308 fltk::setcolor(c3);
309 fltk::fillrect(X,Y,1,1);
310 if(label_flag){
311 fltk::setcolor(c1);
312 char buf[16];
313 if(e->type == MIDI_PITCH_WHEEL){
314 snprintf(buf,16,"%d",M);
316 else{
317 snprintf(buf,16,"%d",mag2val(M));
319 fltk::drawtext(buf,X-fltk::getwidth(buf),Y+12<h()-3?Y+12:h()-3);
322 e = e->next;
325 if(line_flag){
326 fltk::setcolor(fltk::BLUE);
327 fltk::drawline(line_x1,line_y1,line_x2,line_y2);
330 if(box_flag){
331 fltk::setcolor(fltk::GREEN);
332 int X1,X2,Y1,Y2;
333 if(box_x1>box_x2){
334 X1=box_x2;
335 X2=box_x1;
337 else{
338 X1=box_x1;
339 X2=box_x2;
341 if(box_y1>box_y2){
342 Y1=box_y2;
343 Y2=box_y1;
345 else{
346 Y1=box_y1;
347 Y2=box_y2;
349 fltk::fillrect(X1,Y1,X2-X1,1);
350 fltk::fillrect(X1,Y1,1,Y2-Y1);
351 fltk::fillrect(X2,Y1,1,Y2-Y1);
352 fltk::fillrect(X1,Y2,X2-X1,1);
355 if(insert_flag){
356 fltk::setcolor(fltk::CYAN);
357 int X = tick2xpix(insert_t)-scroll;
358 int Y = insert_y;
359 if(Y<0){Y=0;}
360 if(Y>h()-3){Y=h()-3;}
361 int M = insert_M;
362 if(M<0){M=0;}
363 if(M>MAG_MAX){M=MAG_MAX;}
364 fltk::fillrect(X,Y,2,h()-Y);
365 if(label_flag){
366 char buf[16];
367 if(event_type == MIDI_PITCH_WHEEL){
368 snprintf(buf,16,"%d",M);
370 else{
371 snprintf(buf,16,"%d",mag2val(M));
373 fltk::drawtext(buf,X-fltk::getwidth(buf),Y+12<h()-3?Y+12:h()-3);
378 fltk::pop_clip();
383 void EventEdit::load(seqpat* s){
384 cur_seqpat = s;
385 cur_track = tracks[s->track];
386 recount_has();
389 int EventEdit::tick2xpix(int tick){
390 return tick*zoom*4 / 128;
393 const char* EventEdit::event_type_name(){
394 switch(event_type){
395 case MIDI_NOTE_OFF:
396 return "note off velocity";
397 case MIDI_NOTE_ON:
398 return "note on velocity";
399 case MIDI_AFTERTOUCH:
400 return "polyphonic key pressure (aftertouch)";
401 case MIDI_CONTROLLER_CHANGE:
402 return controller_names[controller_type];
403 case MIDI_PROGRAM_CHANGE:
404 return "program change";
405 case MIDI_CHANNEL_PRESSURE:
406 return "channel pressure";
407 case MIDI_PITCH_WHEEL:
408 return "pitch wheel";
409 default:
410 return "booya";
414 void EventEdit::event_type_next(){
415 switch(event_type){
416 case 0x80:
417 event_type = 0xA0;
418 break;
419 case 0x90:
420 event_type = 0x80;
421 break;
422 case 0xA0:
423 event_type = 0xC0;
424 break;
425 case 0xB0:
426 if(controller_type == 127){
427 event_type = 0x90;
429 else{
430 controller_type++;
432 break;
433 case 0xE0:
434 event_type = 0xB0;
435 controller_type = 0;
436 break;
437 default:
438 event_type += 0x10;
442 void EventEdit::event_type_prev(){
443 switch(event_type){
444 case 0x80:
445 event_type = 0x90;
446 break;
447 case 0x90:
448 event_type = 0xB0;
449 controller_type = 127;
450 break;
451 case 0xC0:
452 event_type = 0xA0;
453 break;
454 case 0xB0:
455 if(controller_type == 0){
456 event_type = 0xE0;
458 else{
459 controller_type--;
461 break;
462 case 0xA0:
463 event_type = 0x80;
464 break;
465 default:
466 event_type -= 0x10;
470 void EventEdit::set_event_type(int type, int controller){
471 event_type = type;
472 controller_type = controller;
476 int EventEdit::ypix2mag(int ypix){
477 int H = h()-3;
478 int R = ypix*MAG_MAX/H;
479 return MAG_MAX-R;
482 int EventEdit::mag2ypix(int mag){
483 int H = mag*(h()-3)/MAG_MAX;
484 return h()-H-3;
487 int EventEdit::mag2val(int mag){
488 return mag*127/MAG_MAX;
491 int EventEdit::val2mag(int val){
492 return val*MAG_MAX/127;
495 void EventEdit::apply_line(){
496 mevent* e = cur_seqpat->p->events;
497 Command* c;
498 int N = 0;
499 int T1, T2;
500 int M1 = line_M1;
501 int M2 = line_M2;
502 if(line_t1>line_t2){
503 T1=line_t2;
504 T2=line_t1;
506 else{
507 T1=line_t1;
508 T2=line_t2;
510 while(e->tick < T1){
511 e = e->next;
512 if(!e){
513 return;
516 while(e){
517 if(e->tick > T2){
518 break;
520 if(match_event_type(e) && (e->selected || !select_flag)){
521 float m = (float)(M2-M1)/(T2-T1);
522 float b = M1 - m*T1;
523 int M = (int)(m*e->tick + b);
524 int V1, V2;
525 if(M<0){M=0;}
526 if(M>MAG_MAX){M=MAG_MAX;}
527 switch(e->type){
528 case MIDI_NOTE_OFF:
529 case MIDI_NOTE_ON:
530 case MIDI_AFTERTOUCH:
531 case MIDI_CONTROLLER_CHANGE:
532 V1 = e->value1;
533 V2 = mag2val(M);
534 break;
535 case MIDI_PROGRAM_CHANGE:
536 case MIDI_CHANNEL_PRESSURE:
537 V1 = mag2val(M);
538 break;
539 case MIDI_PITCH_WHEEL:
540 V1 = M&0x7f;
541 V2 = (M&0x3f80) >> 7;
542 break;
544 c = new ChangeEvent(e,V1,V2);
545 set_undo(c);
546 N++;
548 e = e->next;
550 undo_push(N);
553 void EventEdit::apply_box(){
554 select_flag=1;
555 mevent* e = cur_seqpat->p->events->next;
556 int T1=box_t1;
557 int T2=box_t2;
558 int M1 = box_m1;
559 int M2 = box_m2;
560 int tmp;
561 while(e){
562 if(e->tick > T2){break;}
563 int M = get_event_mag(e);
564 if(T1>T2){SWAP(T1,T2);}
565 if(M1<M2){SWAP(M1,M2);}
566 if(e->tick > T1 && e->tick < T2 && M > M2){
567 e->selected = 1;
569 e=e->next;
573 void EventEdit::apply_insert(){
574 int V1,V2;
575 Command* c;
576 get_event_value(&V1,&V2);
577 c = new CreateEvent(cur_seqpat->p,event_type,insert_t,V1,V2);
578 set_undo(c);
579 undo_push(1);
581 switch(event_type){
582 case MIDI_NOTE_ON: has[0]=1; break;
583 case MIDI_NOTE_OFF: has[1]=1; break;
584 case MIDI_AFTERTOUCH: has[2]=1; break;
585 case MIDI_PROGRAM_CHANGE: has[3]=1; break;
586 case MIDI_CHANNEL_PRESSURE: has[4]=1; break;
587 case MIDI_PITCH_WHEEL: has[5]=1; break;
588 default: has[controller_type+6]=1; break;
591 if(event_type==MIDI_NOTE_ON){
592 ui->piano_roll->redraw();
596 void EventEdit::delete_events(int (EventEdit::*pred)(mevent* e)){
597 mevent* e = cur_seqpat->p->events->next;
598 mevent* next;
599 Command* c;
600 int N=0;
601 while(e){
602 if(((this)->*(pred))(e)){
603 next = e->next;
604 c = new DeleteEvent(e);
605 set_undo(c);
606 N++;
607 e = next;
609 else{
610 e = e->next;
613 undo_push(N);
617 int EventEdit::delete_type_in_range_pred(mevent* e){
618 if(e->tick > delete_t1 && e->tick < delete_t2 && match_event_type(e))
619 return 1;
620 else
621 return 0;
624 void EventEdit::apply_delete(){
625 delete_events(&EventEdit::delete_type_in_range_pred);
626 ui->piano_roll->redraw();
631 void EventEdit::apply_paste(){
632 printf("apply paste\n");
635 int EventEdit::match_event_type(mevent* e){
636 if(e->type == event_type){
637 if(e->type == MIDI_CONTROLLER_CHANGE){
638 if(e->value1 == controller_type){
639 return 1;
642 else{
643 return 1;
646 return 0;
649 void EventEdit::get_event_color(mevent* e, fltk::Color* c1, fltk::Color* c2, fltk::Color* c3){
651 int T1,T2;
652 int tmp;
653 if(delete_flag){
654 T1=delete_t1;
655 T2=delete_t2;
656 if(T1>T2){SWAP(T1,T2);}
657 if(e->tick > T1 && e->tick < T2){
658 *c1 = fltk::color(229,79,75);
659 *c2 = fltk::color(120,60,58);
660 *c3 = fltk::color(225,131,109);
661 return;
665 if(box_flag){
666 T1=box_t1;
667 T2=box_t2;
668 int M1 = box_m1;
669 int M2 = box_m2;
670 int M = get_event_mag(e);
671 if(T1>T2){SWAP(T1,T2);}
672 if(M1<M2){SWAP(M1,M2);}
673 if(e->tick > T1 && e->tick < T2 && M > M2){
674 *c1 = fltk::color(108,229,75);
675 *c2 = fltk::color(71,120,59);
676 *c3 = fltk::color(108,229,75);
677 return;
681 if(line_flag){
682 T1=line_t1;
683 T2=line_t2;
684 if(T1>T2){SWAP(T1,T2);}
685 if(e->tick > T1 && e->tick < T2){
686 if(!select_flag || e->selected){
687 *c1 = fltk::color(75,119,229);
688 *c2 = fltk::color(58,76,120);
689 *c3 = fltk::color(109,123,225);
690 return;
695 if(e->selected){
696 *c1 = fltk::color(255,248,47);
697 *c2 = fltk::color(140,137,46);
698 *c3 = fltk::color(232,255,37);
699 return;
702 *c1 = fltk::color(169,75,229);
703 *c2 = fltk::color(95,58,119);
704 *c3 = fltk::color(198,109,225);
707 void EventEdit::get_event_value(int* v1, int* v2){
708 int M = insert_M;
709 if(M<0){M=0;}
710 if(M>MAG_MAX){M=MAG_MAX;}
711 switch(event_type){
712 case MIDI_NOTE_OFF:
713 case MIDI_NOTE_ON:
714 case MIDI_AFTERTOUCH:
715 *v1 = 60;
716 *v2 = mag2val(M);
717 break;
718 case MIDI_CONTROLLER_CHANGE:
719 *v1 = controller_type;
720 *v2 = mag2val(M);
721 break;
722 case MIDI_PROGRAM_CHANGE:
723 case MIDI_CHANNEL_PRESSURE:
724 *v1 = mag2val(M);
725 break;
726 case MIDI_PITCH_WHEEL:
727 *v1 = M&0x7f;
728 *v2 = (M&0x3f80) >> 7;
729 break;
733 int EventEdit::xpix2tick(int xpix){
734 ui->piano_roll->xpix2tick(xpix+scroll);
737 int EventEdit::get_event_mag(mevent* e){
738 switch(e->type){
739 case MIDI_NOTE_OFF:
740 case MIDI_NOTE_ON:
741 case MIDI_AFTERTOUCH:
742 case MIDI_CONTROLLER_CHANGE:
743 return val2mag(e->value2);
744 case MIDI_PROGRAM_CHANGE:
745 case MIDI_CHANNEL_PRESSURE:
746 return val2mag(e->value1);
747 case MIDI_PITCH_WHEEL:
748 return e->value1 | (e->value2<<7);
750 return 0;
754 int EventEdit::delete_type_all_pred(mevent* e){
755 if(match_event_type(e))
756 return 1;
757 else
758 return 0;
761 int EventEdit::delete_all_non_note_pred(mevent* e){
762 if(e->type != MIDI_NOTE_ON && e->type != MIDI_NOTE_OFF)
763 return 1;
764 else
765 return 0;
768 int EventEdit::delete_all_pred(mevent* e){
769 return 1;
772 void EventEdit::clear_events(){
773 delete_events(&EventEdit::delete_type_all_pred);
774 switch(event_type){
775 case MIDI_NOTE_ON: has[0]=0; break;
776 case MIDI_NOTE_OFF: has[1]=0; break;
777 case MIDI_AFTERTOUCH: has[2]=0; break;
778 case MIDI_PROGRAM_CHANGE: has[3]=0; break;
779 case MIDI_CHANNEL_PRESSURE: has[4]=0; break;
780 case MIDI_PITCH_WHEEL: has[5]=0; break;
781 default: has[controller_type+6]=0; break;
783 redraw();
784 ui->piano_roll->redraw();
785 ui->event_menu->redraw();
788 void EventEdit::clear_non_note_events(){
789 delete_events(&EventEdit::delete_all_non_note_pred);
790 redraw();
791 ui->piano_roll->redraw();
792 for(int i=2;i<134;i++){
793 has[i]=0;
795 ui->event_menu->redraw();
798 void EventEdit::clear_all_events(){
799 delete_events(&EventEdit::delete_all_pred);
800 redraw();
801 ui->piano_roll->redraw();
802 for(int i=0;i<134;i++){
803 has[i]=0;
805 ui->event_menu->redraw();
808 void EventEdit::clear_selected_events(){
812 void EventEdit::clear_selection(){
813 select_flag = 0;
814 mevent* e = cur_seqpat->p->events->next;
815 while(e){
816 e->selected=0;
817 e = e->next;
819 redraw();
820 ui->piano_roll->redraw();
823 int EventEdit::quantize(int tick){
824 return ui->piano_roll->quantize(tick);
827 void EventEdit::recount_has(){
828 for(int i=0; i<134; i++){has[i]=0;}
829 mevent* e = cur_seqpat->p->events->next;
830 while(e){
831 switch(e->type){
832 case MIDI_NOTE_ON: has[0]=1; break;
833 case MIDI_NOTE_OFF: has[1]=1; break;
834 case MIDI_AFTERTOUCH: has[2]=1; break;
835 case MIDI_PROGRAM_CHANGE: has[3]=1; break;
836 case MIDI_CHANNEL_PRESSURE: has[4]=1; break;
837 case MIDI_PITCH_WHEEL: has[5]=1; break;
838 default: has[e->value1+6]=1; break;
840 e = e->next;