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
++){
77 int EventEdit::handle(int event
){
86 else if(event_dy() > 0){
95 if(event_button()==1){
96 if(event_state()&fltk::CTRL
){//insert
100 insert_t
= quantize(xpix2tick(X
));
101 insert_M
= ypix2mag(Y
);
103 else if(event_state()&fltk::SHIFT
){//box select
120 line_t1
=xpix2tick(X
);
126 else if(event_button()==2){//paste
129 paste_t
= xpix2tick(X
);
131 else if(event_button()==3){//delete
135 delete_t1
= xpix2tick(X
);
136 delete_t2
= delete_t1
;
147 line_t2
= xpix2tick(X
);
148 line_M2
= ypix2mag(Y
);
153 box_t2
= xpix2tick(X
);
154 box_m2
= ypix2mag(Y
);
159 insert_t
= quantize(xpix2tick(X
));
160 insert_M
= ypix2mag(Y
);
164 delete_t2
= xpix2tick(X
);
168 paste_t
= xpix2tick(X
);
173 if(event_button()==1){//insert, box, line
180 ui
->piano_roll
->redraw();
188 else if(event_button()==2){//complete paste
192 else if(event_button()==3){//delete
203 void EventEdit::draw(){
204 fltk::setfont(fltk::HELVETICA
,12);
205 fltk::setcolor(fltk::GRAY05
);
206 fltk::fillrect(0,0,w(),h());
208 fltk::push_clip(0,0,w(),h());
211 fltk::setcolor(fltk::color(64,0,0));
213 if(delete_x1
>delete_x2
){
221 fltk::fillrect(X1
,0,X2
-X1
,h());
224 fltk::setcolor(fltk::GRAY20
);
225 fltk::drawtext(event_type_name(), 2, h()-5);
227 fltk::setcolor(fltk::GRAY20
);
228 fltk::fillrect(0,h()-3,w(),1);
229 for(int i
=zoom
- scroll
; i
<w(); i
+=zoom
){
231 fltk::drawline(i
,0,i
,h()-1);
235 fltk::setcolor(fltk::GRAY50
);
236 for(int i
=zoom
*4-scroll
; i
<w(); i
+=zoom
*4){
238 fltk::drawline(i
,0,i
,h()-1);
242 fltk::setcolor(fltk::WHITE
);
243 int M
= config
.beats_per_measure
;
245 for(int i
=1; I
<w(); i
++){
246 I
= i
*zoom
*4*M
- scroll
;
248 fltk::fillrect(I
,0,1,h());
252 fltk::setcolor(fltk::color(128,0,0));
253 int rightend
= tick2xpix(cur_seqpat
->dur
)-scroll
;
254 if(rightend
>=0 && rightend
< w()){
255 fltk::fillrect(rightend
,0,1,h());
258 mevent
* e
= cur_seqpat
->p
->events
->next
;
261 if(e
->type
==event_type
){
262 if(e
->type
==MIDI_CONTROLLER_CHANGE
){
263 if(e
->value1
== controller_type
){
264 M
= val2mag(e
->value2
);
276 case MIDI_PROGRAM_CHANGE
:
277 case MIDI_CHANNEL_PRESSURE
:
278 M
= val2mag(e
->value1
);
281 M
= val2mag(e
->value2
);
297 if(line_flag
&& e
->tick
> T1
&& e
->tick
< T2
){
298 if(!select_flag
|| e
->selected
){
299 float m
= (float)(M2
-M1
)/(T2
-T1
);
301 M
= (int)(m
*e
->tick
+ b
);
303 if(M
>MAG_MAX
){M
=MAG_MAX
;}
306 int X
= tick2xpix(e
->tick
) - scroll
;
310 fltk::Color c1
,c2
,c3
;
314 get_event_color(e
,&c1
,&c2
,&c3
);
317 fltk::fillrect(X
,Y
+1,1,H
);
318 fltk::fillrect(X
+1,Y
,1,1);
320 fltk::fillrect(X
+1,Y
+1,1,H
);
322 fltk::fillrect(X
,Y
,1,1);
326 if(e
->type
== MIDI_PITCH_WHEEL
){
327 snprintf(buf
,16,"%d",M
);
330 snprintf(buf
,16,"%d",mag2val(M
));
332 fltk::drawtext(buf
,X
+2,Y
+12<h()-3?Y
+12:h()-3);
340 fltk::setcolor(fltk::BLUE
);
341 fltk::drawline(line_x1
,line_y1
,line_x2
,line_y2
);
345 fltk::setcolor(fltk::GREEN
);
363 fltk::fillrect(X1
,Y1
,X2
-X1
,1);
364 fltk::fillrect(X1
,Y1
,1,Y2
-Y1
);
365 fltk::fillrect(X2
,Y1
,1,Y2
-Y1
);
366 fltk::fillrect(X1
,Y2
,X2
-X1
,1);
370 fltk::setcolor(fltk::CYAN
);
371 int X
= tick2xpix(insert_t
)-scroll
;
374 if(Y
>h()-3){Y
=h()-3;}
377 if(M
>MAG_MAX
){M
=MAG_MAX
;}
378 fltk::fillrect(X
,Y
,2,h()-Y
);
381 if(event_type
== MIDI_PITCH_WHEEL
){
382 snprintf(buf
,16,"%d",M
);
385 snprintf(buf
,16,"%d",mag2val(M
));
387 fltk::drawtext(buf
,X
-fltk::getwidth(buf
),Y
+12<h()-3?Y
+12:h()-3);
397 void EventEdit::load(seqpat
* s
){
399 cur_track
= tracks
[s
->track
];
403 int EventEdit::tick2xpix(int tick
){
404 return tick
*zoom
*4 / 128;
407 const char* EventEdit::event_type_name(){
410 return "note off velocity";
412 return "note on velocity";
413 case MIDI_AFTERTOUCH
:
414 return "polyphonic key pressure (aftertouch)";
415 case MIDI_CONTROLLER_CHANGE
:
416 return controller_names
[controller_type
];
417 case MIDI_PROGRAM_CHANGE
:
418 return "program change";
419 case MIDI_CHANNEL_PRESSURE
:
420 return "channel pressure";
421 case MIDI_PITCH_WHEEL
:
422 return "pitch wheel";
428 void EventEdit::event_type_next(){
440 if(controller_type
== 127){
456 void EventEdit::event_type_prev(){
463 controller_type
= 127;
469 if(controller_type
== 0){
484 void EventEdit::set_event_type(int type
, int controller
){
486 controller_type
= controller
;
490 int EventEdit::ypix2mag(int ypix
){
492 int R
= ypix
*MAG_MAX
/H
;
496 int EventEdit::mag2ypix(int mag
){
497 int H
= mag
*(h()-3)/MAG_MAX
;
501 int EventEdit::mag2val(int mag
){
502 return mag
*127/MAG_MAX
;
505 int EventEdit::val2mag(int val
){
506 return val
*MAG_MAX
/127;
509 void EventEdit::apply_line(){
510 mevent
* e
= cur_seqpat
->p
->events
;
534 if(match_event_type(e
) && (e
->selected
|| !select_flag
)){
535 float m
= (float)(M2
-M1
)/(T2
-T1
);
537 int M
= (int)(m
*e
->tick
+ b
);
540 if(M
>MAG_MAX
){M
=MAG_MAX
;}
544 case MIDI_AFTERTOUCH
:
545 case MIDI_CONTROLLER_CHANGE
:
549 case MIDI_PROGRAM_CHANGE
:
550 case MIDI_CHANNEL_PRESSURE
:
553 case MIDI_PITCH_WHEEL
:
555 V2
= (M
&0x3f80) >> 7;
558 c
= new ChangeEvent(e
,V1
,V2
);
567 void EventEdit::apply_box(){
569 mevent
* e
= cur_seqpat
->p
->events
->next
;
576 if(e
->tick
> T2
){break;}
577 int M
= get_event_mag(e
);
578 if(T1
>T2
){SWAP(T1
,T2
);}
579 if(M1
<M2
){SWAP(M1
,M2
);}
580 if(e
->tick
> T1
&& e
->tick
< T2
&& M
> M2
){
587 void EventEdit::apply_insert(){
590 get_event_value(&V1
,&V2
);
591 c
= new CreateEvent(cur_seqpat
->p
,event_type
,insert_t
,V1
,V2
);
596 case MIDI_NOTE_ON
: has
[0]=1; break;
597 case MIDI_NOTE_OFF
: has
[1]=1; break;
598 case MIDI_AFTERTOUCH
: has
[2]=1; break;
599 case MIDI_PROGRAM_CHANGE
: has
[3]=1; break;
600 case MIDI_CHANNEL_PRESSURE
: has
[4]=1; break;
601 case MIDI_PITCH_WHEEL
: has
[5]=1; break;
602 default: has
[controller_type
+6]=1; break;
605 if(event_type
==MIDI_NOTE_ON
){
606 ui
->piano_roll
->redraw();
610 void EventEdit::delete_events(int (EventEdit::*pred
)(mevent
* e
)){
611 mevent
* e
= cur_seqpat
->p
->events
->next
;
616 if(((this)->*(pred
))(e
)){
618 c
= new DeleteEvent(e
);
631 int EventEdit::delete_type_in_range_pred(mevent
* e
){
632 if(e
->tick
> delete_t1
&& e
->tick
< delete_t2
&& match_event_type(e
))
638 void EventEdit::apply_delete(){
639 delete_events(&EventEdit::delete_type_in_range_pred
);
640 ui
->piano_roll
->redraw();
645 void EventEdit::apply_paste(){
646 printf("apply paste\n");
649 int EventEdit::match_event_type(mevent
* e
){
650 if(e
->type
== event_type
){
651 if(e
->type
== MIDI_CONTROLLER_CHANGE
){
652 if(e
->value1
== controller_type
){
663 void EventEdit::get_event_color(mevent
* e
, fltk::Color
* c1
, fltk::Color
* c2
, fltk::Color
* c3
){
670 if(T1
>T2
){SWAP(T1
,T2
);}
671 if(e
->tick
> T1
&& e
->tick
< T2
){
672 *c1
= fltk::color(229,79,75);
673 *c2
= fltk::color(120,60,58);
674 *c3
= fltk::color(225,131,109);
684 int M
= get_event_mag(e
);
685 if(T1
>T2
){SWAP(T1
,T2
);}
686 if(M1
<M2
){SWAP(M1
,M2
);}
687 if(e
->tick
> T1
&& e
->tick
< T2
&& M
> M2
){
688 *c1
= fltk::color(108,229,75);
689 *c2
= fltk::color(71,120,59);
690 *c3
= fltk::color(108,229,75);
698 if(T1
>T2
){SWAP(T1
,T2
);}
699 if(e
->tick
> T1
&& e
->tick
< T2
){
700 if(!select_flag
|| e
->selected
){
701 *c1
= fltk::color(75,119,229);
702 *c2
= fltk::color(58,76,120);
703 *c3
= fltk::color(109,123,225);
710 *c1
= fltk::color(255,248,47);
711 *c2
= fltk::color(140,137,46);
712 *c3
= fltk::color(232,255,37);
716 *c1
= fltk::color(169,75,229);
717 *c2
= fltk::color(95,58,119);
718 *c3
= fltk::color(198,109,225);
721 void EventEdit::get_event_value(int* v1
, int* v2
){
724 if(M
>MAG_MAX
){M
=MAG_MAX
;}
728 case MIDI_AFTERTOUCH
:
732 case MIDI_CONTROLLER_CHANGE
:
733 *v1
= controller_type
;
736 case MIDI_PROGRAM_CHANGE
:
737 case MIDI_CHANNEL_PRESSURE
:
740 case MIDI_PITCH_WHEEL
:
742 *v2
= (M
&0x3f80) >> 7;
747 int EventEdit::xpix2tick(int xpix
){
748 ui
->piano_roll
->xpix2tick(xpix
+scroll
);
751 int EventEdit::get_event_mag(mevent
* e
){
755 case MIDI_AFTERTOUCH
:
756 case MIDI_CONTROLLER_CHANGE
:
757 return val2mag(e
->value2
);
758 case MIDI_PROGRAM_CHANGE
:
759 case MIDI_CHANNEL_PRESSURE
:
760 return val2mag(e
->value1
);
761 case MIDI_PITCH_WHEEL
:
762 return e
->value1
| (e
->value2
<<7);
768 int EventEdit::delete_type_all_pred(mevent
* e
){
769 if(match_event_type(e
))
775 int EventEdit::delete_all_non_note_pred(mevent
* e
){
776 if(e
->type
!= MIDI_NOTE_ON
&& e
->type
!= MIDI_NOTE_OFF
)
782 int EventEdit::delete_all_pred(mevent
* e
){
786 void EventEdit::clear_events(){
787 delete_events(&EventEdit::delete_type_all_pred
);
789 case MIDI_NOTE_ON
: has
[0]=0; break;
790 case MIDI_NOTE_OFF
: has
[1]=0; break;
791 case MIDI_AFTERTOUCH
: has
[2]=0; break;
792 case MIDI_PROGRAM_CHANGE
: has
[3]=0; break;
793 case MIDI_CHANNEL_PRESSURE
: has
[4]=0; break;
794 case MIDI_PITCH_WHEEL
: has
[5]=0; break;
795 default: has
[controller_type
+6]=0; break;
798 ui
->piano_roll
->redraw();
799 ui
->event_menu
->redraw();
802 void EventEdit::clear_non_note_events(){
803 delete_events(&EventEdit::delete_all_non_note_pred
);
805 ui
->piano_roll
->redraw();
806 for(int i
=2;i
<134;i
++){
809 ui
->event_menu
->redraw();
812 void EventEdit::clear_all_events(){
813 delete_events(&EventEdit::delete_all_pred
);
815 ui
->piano_roll
->redraw();
816 for(int i
=0;i
<134;i
++){
819 ui
->event_menu
->redraw();
822 void EventEdit::clear_selected_events(){
826 void EventEdit::clear_selection(){
828 mevent
* e
= cur_seqpat
->p
->events
->next
;
834 ui
->piano_roll
->redraw();
837 int EventEdit::quantize(int tick
){
838 return ui
->piano_roll
->quantize(tick
);
841 void EventEdit::recount_has(){
842 for(int i
=0; i
<134; i
++){has
[i
]=0;}
843 mevent
* e
= cur_seqpat
->p
->events
->next
;
846 case MIDI_NOTE_ON
: has
[0]=1; break;
847 case MIDI_NOTE_OFF
: has
[1]=1; break;
848 case MIDI_AFTERTOUCH
: has
[2]=1; break;
849 case MIDI_PROGRAM_CHANGE
: has
[3]=1; break;
850 case MIDI_CHANNEL_PRESSURE
: has
[4]=1; break;
851 case MIDI_PITCH_WHEEL
: has
[5]=1; break;
852 default: has
[e
->value1
+6]=1; break;