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
27 #include <fltk/Group.h>
28 #include <fltk/Widget.h>
29 #include <fltk/events.h>
33 #include "eventedit.h"
40 #define SWAP(X,Y) tmp=X; X=Y; Y=tmp;
42 extern struct conf config
;
45 extern std::vector
<track
*> tracks
;
47 extern char controller_names
[128][64];
51 EventEdit::EventEdit(int x
, int y
, int w
, int h
, const char* label
= 0) : fltk::Widget(x
, y
, w
, h
, label
) {
55 event_type
= MIDI_NOTE_ON
;
67 for(int i
=0; i
<134; i
++){
72 int EventEdit::handle(int event
){
81 else if(event_dy() > 0){
90 if(event_button()==1){
91 if(event_state()&fltk::CTRL
){//insert
95 insert_t
= quantize(xpix2tick(X
));
96 insert_M
= ypix2mag(Y
);
98 else if(event_state()&fltk::SHIFT
){//box select
115 line_t1
=xpix2tick(X
);
121 else if(event_button()==2){//paste
124 paste_t
= xpix2tick(X
);
126 else if(event_button()==3){//delete
130 delete_t1
= xpix2tick(X
);
131 delete_t2
= delete_t1
;
142 line_t2
= xpix2tick(X
);
143 line_M2
= ypix2mag(Y
);
148 box_t2
= xpix2tick(X
);
149 box_m2
= ypix2mag(Y
);
154 insert_t
= quantize(xpix2tick(X
));
155 insert_M
= ypix2mag(Y
);
159 delete_t2
= xpix2tick(X
);
163 paste_t
= xpix2tick(X
);
168 if(event_button()==1){//insert, box, line
175 ui
->piano_roll
->redraw();
183 else if(event_button()==2){//complete paste
187 else if(event_button()==3){//delete
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());
206 fltk::setcolor(fltk::color(64,0,0));
208 if(delete_x1
>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
){
226 fltk::drawline(i
,0,i
,h()-1);
230 fltk::setcolor(fltk::GRAY50
);
231 for(int i
=zoom
*4-scroll
; i
<w(); i
+=zoom
*4){
233 fltk::drawline(i
,0,i
,h()-1);
237 fltk::setcolor(fltk::WHITE
);
238 int M
= config
.beats_per_measure
;
240 for(int i
=1; I
<w(); i
++){
241 I
= i
*zoom
*4*M
- scroll
;
243 fltk::fillrect(I
,0,1,h());
247 fltk::setcolor(fltk::color(128,0,0));
248 int rightend
= tick2xpix(cur_seqpat
->dur
)-scroll
;
249 if(rightend
>=0 && rightend
< w()){
250 fltk::fillrect(rightend
,0,1,h());
253 mevent
* e
= cur_seqpat
->p
->events
->next
;
256 if(e
->type
==event_type
){
257 if(e
->type
==MIDI_CONTROLLER_CHANGE
){
258 if(e
->value1
== controller_type
){
259 M
= val2mag(e
->value2
);
271 case MIDI_PROGRAM_CHANGE
:
272 case MIDI_CHANNEL_PRESSURE
:
273 M
= val2mag(e
->value1
);
276 M
= val2mag(e
->value2
);
292 if(line_flag
&& e
->tick
> T1
&& e
->tick
< T2
){
293 if(!select_flag
|| e
->selected
){
294 float m
= (float)(M2
-M1
)/(T2
-T1
);
296 M
= (int)(m
*e
->tick
+ b
);
298 if(M
>MAG_MAX
){M
=MAG_MAX
;}
301 int X
= tick2xpix(e
->tick
) - scroll
;
305 fltk::Color c1
,c2
,c3
;
309 get_event_color(e
,&c1
,&c2
,&c3
);
312 fltk::fillrect(X
,Y
+1,1,H
);
313 fltk::fillrect(X
+1,Y
,1,1);
315 fltk::fillrect(X
+1,Y
+1,1,H
);
317 fltk::fillrect(X
,Y
,1,1);
321 if(e
->type
== MIDI_PITCH_WHEEL
){
322 snprintf(buf
,16,"%d",M
);
325 snprintf(buf
,16,"%d",mag2val(M
));
327 fltk::drawtext(buf
,X
-fltk::getwidth(buf
),Y
+12<h()-3?Y
+12:h()-3);
334 fltk::setcolor(fltk::BLUE
);
335 fltk::drawline(line_x1
,line_y1
,line_x2
,line_y2
);
339 fltk::setcolor(fltk::GREEN
);
357 fltk::fillrect(X1
,Y1
,X2
-X1
,1);
358 fltk::fillrect(X1
,Y1
,1,Y2
-Y1
);
359 fltk::fillrect(X2
,Y1
,1,Y2
-Y1
);
360 fltk::fillrect(X1
,Y2
,X2
-X1
,1);
364 fltk::setcolor(fltk::CYAN
);
365 int X
= tick2xpix(insert_t
)-scroll
;
368 if(Y
>h()-3){Y
=h()-3;}
371 if(M
>MAG_MAX
){M
=MAG_MAX
;}
372 fltk::fillrect(X
,Y
,2,h()-Y
);
375 if(event_type
== MIDI_PITCH_WHEEL
){
376 snprintf(buf
,16,"%d",M
);
379 snprintf(buf
,16,"%d",mag2val(M
));
381 fltk::drawtext(buf
,X
-fltk::getwidth(buf
),Y
+12<h()-3?Y
+12:h()-3);
391 void EventEdit::load(seqpat
* s
){
393 cur_track
= tracks
[s
->track
];
397 int EventEdit::tick2xpix(int tick
){
398 return tick
*zoom
*4 / 128;
401 const char* EventEdit::event_type_name(){
404 return "note off velocity";
406 return "note on velocity";
407 case MIDI_AFTERTOUCH
:
408 return "polyphonic key pressure (aftertouch)";
409 case MIDI_CONTROLLER_CHANGE
:
410 return controller_names
[controller_type
];
411 case MIDI_PROGRAM_CHANGE
:
412 return "program change";
413 case MIDI_CHANNEL_PRESSURE
:
414 return "channel pressure";
415 case MIDI_PITCH_WHEEL
:
416 return "pitch wheel";
422 void EventEdit::event_type_next(){
434 if(controller_type
== 127){
450 void EventEdit::event_type_prev(){
457 controller_type
= 127;
463 if(controller_type
== 0){
478 void EventEdit::set_event_type(int type
, int controller
){
480 controller_type
= controller
;
484 int EventEdit::ypix2mag(int ypix
){
486 int R
= ypix
*MAG_MAX
/H
;
490 int EventEdit::mag2ypix(int mag
){
491 int H
= mag
*(h()-3)/MAG_MAX
;
495 int EventEdit::mag2val(int mag
){
496 return mag
*127/MAG_MAX
;
499 int EventEdit::val2mag(int val
){
500 return val
*MAG_MAX
/127;
503 void EventEdit::apply_line(){
504 mevent
* e
= cur_seqpat
->p
->events
;
528 if(match_event_type(e
) && (e
->selected
|| !select_flag
)){
529 float m
= (float)(M2
-M1
)/(T2
-T1
);
531 int M
= (int)(m
*e
->tick
+ b
);
534 if(M
>MAG_MAX
){M
=MAG_MAX
;}
538 case MIDI_AFTERTOUCH
:
539 case MIDI_CONTROLLER_CHANGE
:
543 case MIDI_PROGRAM_CHANGE
:
544 case MIDI_CHANNEL_PRESSURE
:
547 case MIDI_PITCH_WHEEL
:
549 V2
= (M
&0x3f80) >> 7;
552 c
= new ChangeEvent(e
,V1
,V2
);
561 void EventEdit::apply_box(){
563 mevent
* e
= cur_seqpat
->p
->events
->next
;
570 if(e
->tick
> T2
){break;}
571 int M
= get_event_mag(e
);
572 if(T1
>T2
){SWAP(T1
,T2
);}
573 if(M1
<M2
){SWAP(M1
,M2
);}
574 if(e
->tick
> T1
&& e
->tick
< T2
&& M
> M2
){
581 void EventEdit::apply_insert(){
584 get_event_value(&V1
,&V2
);
585 c
= new CreateEvent(cur_seqpat
->p
,event_type
,insert_t
,V1
,V2
);
590 case MIDI_NOTE_ON
: has
[0]=1; break;
591 case MIDI_NOTE_OFF
: has
[1]=1; break;
592 case MIDI_AFTERTOUCH
: has
[2]=1; break;
593 case MIDI_PROGRAM_CHANGE
: has
[3]=1; break;
594 case MIDI_CHANNEL_PRESSURE
: has
[4]=1; break;
595 case MIDI_PITCH_WHEEL
: has
[5]=1; break;
596 default: has
[controller_type
+6]=1; break;
599 if(event_type
==MIDI_NOTE_ON
){
600 ui
->piano_roll
->redraw();
604 void EventEdit::delete_events(int (EventEdit::*pred
)(mevent
* e
)){
605 mevent
* e
= cur_seqpat
->p
->events
->next
;
610 if(((this)->*(pred
))(e
)){
612 c
= new DeleteEvent(e
);
625 int EventEdit::delete_type_in_range_pred(mevent
* e
){
626 if(e
->tick
> delete_t1
&& e
->tick
< delete_t2
&& match_event_type(e
))
632 void EventEdit::apply_delete(){
633 delete_events(&EventEdit::delete_type_in_range_pred
);
634 ui
->piano_roll
->redraw();
639 void EventEdit::apply_paste(){
640 printf("apply paste\n");
643 int EventEdit::match_event_type(mevent
* e
){
644 if(e
->type
== event_type
){
645 if(e
->type
== MIDI_CONTROLLER_CHANGE
){
646 if(e
->value1
== controller_type
){
657 void EventEdit::get_event_color(mevent
* e
, fltk::Color
* c1
, fltk::Color
* c2
, fltk::Color
* c3
){
664 if(T1
>T2
){SWAP(T1
,T2
);}
665 if(e
->tick
> T1
&& e
->tick
< T2
){
666 *c1
= fltk::color(229,79,75);
667 *c2
= fltk::color(120,60,58);
668 *c3
= fltk::color(225,131,109);
678 int M
= get_event_mag(e
);
679 if(T1
>T2
){SWAP(T1
,T2
);}
680 if(M1
<M2
){SWAP(M1
,M2
);}
681 if(e
->tick
> T1
&& e
->tick
< T2
&& M
> M2
){
682 *c1
= fltk::color(108,229,75);
683 *c2
= fltk::color(71,120,59);
684 *c3
= fltk::color(108,229,75);
692 if(T1
>T2
){SWAP(T1
,T2
);}
693 if(e
->tick
> T1
&& e
->tick
< T2
){
694 if(!select_flag
|| e
->selected
){
695 *c1
= fltk::color(75,119,229);
696 *c2
= fltk::color(58,76,120);
697 *c3
= fltk::color(109,123,225);
704 *c1
= fltk::color(255,248,47);
705 *c2
= fltk::color(140,137,46);
706 *c3
= fltk::color(232,255,37);
710 *c1
= fltk::color(169,75,229);
711 *c2
= fltk::color(95,58,119);
712 *c3
= fltk::color(198,109,225);
715 void EventEdit::get_event_value(int* v1
, int* v2
){
718 if(M
>MAG_MAX
){M
=MAG_MAX
;}
722 case MIDI_AFTERTOUCH
:
726 case MIDI_CONTROLLER_CHANGE
:
727 *v1
= controller_type
;
730 case MIDI_PROGRAM_CHANGE
:
731 case MIDI_CHANNEL_PRESSURE
:
734 case MIDI_PITCH_WHEEL
:
736 *v2
= (M
&0x3f80) >> 7;
741 int EventEdit::xpix2tick(int xpix
){
742 ui
->piano_roll
->xpix2tick(xpix
+scroll
);
745 int EventEdit::get_event_mag(mevent
* e
){
749 case MIDI_AFTERTOUCH
:
750 case MIDI_CONTROLLER_CHANGE
:
751 return val2mag(e
->value2
);
752 case MIDI_PROGRAM_CHANGE
:
753 case MIDI_CHANNEL_PRESSURE
:
754 return val2mag(e
->value1
);
755 case MIDI_PITCH_WHEEL
:
756 return e
->value1
| (e
->value2
<<7);
762 int EventEdit::delete_type_all_pred(mevent
* e
){
763 if(match_event_type(e
))
769 int EventEdit::delete_all_non_note_pred(mevent
* e
){
770 if(e
->type
!= MIDI_NOTE_ON
&& e
->type
!= MIDI_NOTE_OFF
)
776 int EventEdit::delete_all_pred(mevent
* e
){
780 void EventEdit::clear_events(){
781 delete_events(&EventEdit::delete_type_all_pred
);
783 case MIDI_NOTE_ON
: has
[0]=0; break;
784 case MIDI_NOTE_OFF
: has
[1]=0; break;
785 case MIDI_AFTERTOUCH
: has
[2]=0; break;
786 case MIDI_PROGRAM_CHANGE
: has
[3]=0; break;
787 case MIDI_CHANNEL_PRESSURE
: has
[4]=0; break;
788 case MIDI_PITCH_WHEEL
: has
[5]=0; break;
789 default: has
[controller_type
+6]=0; break;
792 ui
->piano_roll
->redraw();
793 ui
->event_menu
->redraw();
796 void EventEdit::clear_non_note_events(){
797 delete_events(&EventEdit::delete_all_non_note_pred
);
799 ui
->piano_roll
->redraw();
800 for(int i
=2;i
<134;i
++){
803 ui
->event_menu
->redraw();
806 void EventEdit::clear_all_events(){
807 delete_events(&EventEdit::delete_all_pred
);
809 ui
->piano_roll
->redraw();
810 for(int i
=0;i
<134;i
++){
813 ui
->event_menu
->redraw();
816 void EventEdit::clear_selected_events(){
820 void EventEdit::clear_selection(){
822 mevent
* e
= cur_seqpat
->p
->events
->next
;
828 ui
->piano_roll
->redraw();
831 int EventEdit::quantize(int tick
){
832 return ui
->piano_roll
->quantize(tick
);
835 void EventEdit::recount_has(){
836 for(int i
=0; i
<134; i
++){has
[i
]=0;}
837 mevent
* e
= cur_seqpat
->p
->events
->next
;
840 case MIDI_NOTE_ON
: has
[0]=1; break;
841 case MIDI_NOTE_OFF
: has
[1]=1; break;
842 case MIDI_AFTERTOUCH
: has
[2]=1; break;
843 case MIDI_PROGRAM_CHANGE
: has
[3]=1; break;
844 case MIDI_CHANNEL_PRESSURE
: has
[4]=1; break;
845 case MIDI_PITCH_WHEEL
: has
[5]=1; break;
846 default: has
[e
->value1
+6]=1; break;