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"
41 #define SWAP(X,Y) {int tmp=X; X=Y; Y=tmp;}
43 extern struct conf config
;
46 extern std::vector
<track
*> tracks
;
48 extern char controller_names
[128][64];
52 EventEdit::EventEdit(int x
, int y
, int w
, int h
, const char* label
= 0) : fltk::Widget(x
, y
, w
, h
, label
) {
56 event_type
= MIDI_NOTE_ON
;
68 for(int i
=0; i
<134; i
++){
78 int EventEdit::handle(int event
){
87 else if(event_dy() > 0){
96 if(event_button()==1){
97 if(event_state()&fltk::CTRL
){//insert
101 insert_t
= quantize(xpix2tick(X
));
102 insert_M
= ypix2mag(Y
);
104 else if(event_state()&fltk::SHIFT
){//box select
121 line_t1
=xpix2tick(X
);
127 else if(event_button()==2){//paste
130 paste_t
= xpix2tick(X
);
132 else if(event_button()==3){//delete
136 delete_t1
= xpix2tick(X
);
137 delete_t2
= delete_t1
;
148 line_t2
= xpix2tick(X
);
149 line_M2
= ypix2mag(Y
);
154 box_t2
= xpix2tick(X
);
155 box_m2
= ypix2mag(Y
);
160 insert_t
= quantize(xpix2tick(X
));
161 insert_M
= ypix2mag(Y
);
165 delete_t2
= xpix2tick(X
);
169 paste_t
= xpix2tick(X
);
174 if(event_button()==1){//insert, box, line
181 ui
->piano_roll
->redraw();
189 else if(event_button()==2){//complete paste
193 else if(event_button()==3){//delete
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());
212 fltk::setcolor(fltk::color(64,0,0));
214 if(delete_x1
>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
){
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){
239 fltk::drawline(i
,0,i
,h()-1);
243 fltk::setcolor(fltk::WHITE
);
244 int M
= config
.beats_per_measure
;
246 for(int i
=1; I
<w(); i
++){
247 I
= i
*zoom
*4*M
- scroll
;
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
;
262 if(e
->type
==event_type
){
263 if(e
->type
==MIDI_CONTROLLER_CHANGE
){
264 if(e
->value1
== controller_type
){
265 M
= val2mag(e
->value2
);
277 case MIDI_PROGRAM_CHANGE
:
278 case MIDI_CHANNEL_PRESSURE
:
279 M
= val2mag(e
->value1
);
282 M
= val2mag(e
->value2
);
298 if(line_flag
&& e
->tick
> T1
&& e
->tick
< T2
){
299 if(!select_flag
|| e
->selected
){
300 float m
= (float)(M2
-M1
)/(T2
-T1
);
302 M
= (int)(m
*e
->tick
+ b
);
304 if(M
>MAG_MAX
){M
=MAG_MAX
;}
307 int X
= tick2xpix(e
->tick
) - scroll
;
311 fltk::Color c1
,c2
,c3
;
315 get_event_color(e
,&c1
,&c2
,&c3
);
318 fltk::fillrect(X
,Y
+1,1,H
);
319 fltk::fillrect(X
+1,Y
,1,1);
321 fltk::fillrect(X
+1,Y
+1,1,H
);
323 fltk::fillrect(X
,Y
,1,1);
327 if(e
->type
== MIDI_PITCH_WHEEL
){
328 snprintf(buf
,16,"%d",M
);
331 snprintf(buf
,16,"%d",mag2val(M
));
333 fltk::drawtext(buf
,X
+2,Y
+12<h()-3?Y
+12:h()-3);
341 fltk::setcolor(fltk::BLUE
);
342 fltk::drawline(line_x1
,line_y1
,line_x2
,line_y2
);
346 fltk::setcolor(fltk::GREEN
);
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);
371 fltk::setcolor(fltk::CYAN
);
372 int X
= tick2xpix(insert_t
)-scroll
;
375 if(Y
>h()-3){Y
=h()-3;}
378 if(M
>MAG_MAX
){M
=MAG_MAX
;}
379 fltk::fillrect(X
,Y
,2,h()-Y
);
382 if(event_type
== MIDI_PITCH_WHEEL
){
383 snprintf(buf
,16,"%d",M
);
386 snprintf(buf
,16,"%d",mag2val(M
));
388 fltk::drawtext(buf
,X
-fltk::getwidth(buf
),Y
+12<h()-3?Y
+12:h()-3);
398 void EventEdit::load(seqpat
* s
){
400 cur_track
= tracks
[s
->track
];
404 int EventEdit::tick2xpix(int tick
){
405 return tick
*zoom
*4 / TICKS_PER_BEAT
;
408 const char* EventEdit::event_type_name(){
411 return "note off velocity";
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";
429 void EventEdit::event_type_next(){
441 if(controller_type
== 127){
457 void EventEdit::event_type_prev(){
464 controller_type
= 127;
470 if(controller_type
== 0){
485 void EventEdit::set_event_type(int type
, int controller
){
487 controller_type
= controller
;
491 int EventEdit::ypix2mag(int ypix
){
493 int R
= ypix
*MAG_MAX
/H
;
497 int EventEdit::mag2ypix(int mag
){
498 int H
= mag
*(h()-3)/MAG_MAX
;
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
;
519 if(T1
>T2
){SWAP(T1
,T2
);}
520 //if(M1>M2){SWAP(M1,M2);}
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
);
538 if(M
>MAG_MAX
){M
=MAG_MAX
;}
542 case MIDI_AFTERTOUCH
:
543 case MIDI_CONTROLLER_CHANGE
:
547 case MIDI_PROGRAM_CHANGE
:
548 case MIDI_CHANNEL_PRESSURE
:
551 case MIDI_PITCH_WHEEL
:
553 V2
= (M
&0x3f80) >> 7;
556 c
= new ChangeEvent(e
,V1
,V2
);
565 void EventEdit::apply_box(){
567 mevent
* e
= cur_seqpat
->p
->events
->next
;
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
){
585 void EventEdit::apply_insert(){
588 get_event_value(&V1
,&V2
);
589 c
= new CreateEvent(cur_seqpat
->p
,event_type
,insert_t
,V1
,V2
);
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
;
614 if(((this)->*(pred
))(e
)){
616 c
= new DeleteEvent(e
);
629 int EventEdit::delete_type_in_range_pred(mevent
* e
){
634 if(e
->tick
> L
&& e
->tick
< R
&& match_event_type(e
))
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
){
665 void EventEdit::get_event_color(mevent
* e
, fltk::Color
* c1
, fltk::Color
* c2
, fltk::Color
* c3
){
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);
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);
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);
712 *c1
= fltk::color(255,248,47);
713 *c2
= fltk::color(140,137,46);
714 *c3
= fltk::color(232,255,37);
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
){
726 if(M
>MAG_MAX
){M
=MAG_MAX
;}
730 case MIDI_AFTERTOUCH
:
734 case MIDI_CONTROLLER_CHANGE
:
735 *v1
= controller_type
;
738 case MIDI_PROGRAM_CHANGE
:
739 case MIDI_CHANNEL_PRESSURE
:
742 case MIDI_PITCH_WHEEL
:
744 *v2
= (M
&0x3f80) >> 7;
749 int EventEdit::xpix2tick(int xpix
){
750 ui
->piano_roll
->xpix2tick(xpix
+scroll
);
753 int EventEdit::get_event_mag(mevent
* e
){
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);
770 int EventEdit::delete_type_all_pred(mevent
* e
){
771 if(match_event_type(e
))
777 int EventEdit::delete_all_non_note_pred(mevent
* e
){
778 if(e
->type
!= MIDI_NOTE_ON
&& e
->type
!= MIDI_NOTE_OFF
)
784 int EventEdit::delete_all_pred(mevent
* e
){
788 void EventEdit::clear_events(){
789 delete_events(&EventEdit::delete_type_all_pred
);
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;
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
);
807 ui
->piano_roll
->redraw();
808 for(int i
=2;i
<134;i
++){
811 ui
->event_menu
->redraw();
814 void EventEdit::clear_all_events(){
815 delete_events(&EventEdit::delete_all_pred
);
817 ui
->piano_roll
->redraw();
818 for(int i
=0;i
<134;i
++){
821 ui
->event_menu
->redraw();
824 void EventEdit::clear_selected_events(){
828 void EventEdit::clear_selection(){
830 mevent
* e
= cur_seqpat
->p
->events
->next
;
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
;
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;