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
){
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
;
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
;
248 if(e
->type
==event_type
){
249 if(e
->type
==MIDI_CONTROLLER_CHANGE
){
250 if(e
->value1
== controller_type
){
251 M
= val2mag(e
->value2
);
263 case MIDI_PROGRAM_CHANGE
:
264 case MIDI_CHANNEL_PRESSURE
:
265 M
= val2mag(e
->value1
);
268 M
= val2mag(e
->value2
);
284 if(line_flag
&& e
->tick
> T1
&& e
->tick
< T2
){
285 if(!select_flag
|| e
->selected
){
286 float m
= (float)(M2
-M1
)/(T2
-T1
);
288 M
= (int)(m
*e
->tick
+ b
);
290 if(M
>MAG_MAX
){M
=MAG_MAX
;}
293 int X
= tick2xpix(e
->tick
) - scroll
;
297 fltk::Color c1
,c2
,c3
;
301 get_event_color(e
,&c1
,&c2
,&c3
);
304 fltk::fillrect(X
,Y
+1,1,H
);
305 fltk::fillrect(X
+1,Y
,1,1);
307 fltk::fillrect(X
+1,Y
+1,1,H
);
309 fltk::fillrect(X
,Y
,1,1);
313 if(e
->type
== MIDI_PITCH_WHEEL
){
314 snprintf(buf
,16,"%d",M
);
317 snprintf(buf
,16,"%d",mag2val(M
));
319 fltk::drawtext(buf
,X
-fltk::getwidth(buf
),Y
+12<h()-3?Y
+12:h()-3);
326 fltk::setcolor(fltk::BLUE
);
327 fltk::drawline(line_x1
,line_y1
,line_x2
,line_y2
);
331 fltk::setcolor(fltk::GREEN
);
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);
356 fltk::setcolor(fltk::CYAN
);
357 int X
= tick2xpix(insert_t
)-scroll
;
360 if(Y
>h()-3){Y
=h()-3;}
363 if(M
>MAG_MAX
){M
=MAG_MAX
;}
364 fltk::fillrect(X
,Y
,2,h()-Y
);
367 if(event_type
== MIDI_PITCH_WHEEL
){
368 snprintf(buf
,16,"%d",M
);
371 snprintf(buf
,16,"%d",mag2val(M
));
373 fltk::drawtext(buf
,X
-fltk::getwidth(buf
),Y
+12<h()-3?Y
+12:h()-3);
383 void EventEdit::load(seqpat
* s
){
385 cur_track
= tracks
[s
->track
];
389 int EventEdit::tick2xpix(int tick
){
390 return tick
*zoom
*4 / 128;
393 const char* EventEdit::event_type_name(){
396 return "note off velocity";
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";
414 void EventEdit::event_type_next(){
426 if(controller_type
== 127){
442 void EventEdit::event_type_prev(){
449 controller_type
= 127;
455 if(controller_type
== 0){
470 void EventEdit::set_event_type(int type
, int controller
){
472 controller_type
= controller
;
476 int EventEdit::ypix2mag(int ypix
){
478 int R
= ypix
*MAG_MAX
/H
;
482 int EventEdit::mag2ypix(int mag
){
483 int H
= mag
*(h()-3)/MAG_MAX
;
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
;
520 if(match_event_type(e
) && (e
->selected
|| !select_flag
)){
521 float m
= (float)(M2
-M1
)/(T2
-T1
);
523 int M
= (int)(m
*e
->tick
+ b
);
526 if(M
>MAG_MAX
){M
=MAG_MAX
;}
530 case MIDI_AFTERTOUCH
:
531 case MIDI_CONTROLLER_CHANGE
:
535 case MIDI_PROGRAM_CHANGE
:
536 case MIDI_CHANNEL_PRESSURE
:
539 case MIDI_PITCH_WHEEL
:
541 V2
= (M
&0x3f80) >> 7;
544 c
= new ChangeEvent(e
,V1
,V2
);
553 void EventEdit::apply_box(){
555 mevent
* e
= cur_seqpat
->p
->events
->next
;
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
){
573 void EventEdit::apply_insert(){
576 get_event_value(&V1
,&V2
);
577 c
= new CreateEvent(cur_seqpat
->p
,event_type
,insert_t
,V1
,V2
);
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
;
602 if(((this)->*(pred
))(e
)){
604 c
= new DeleteEvent(e
);
617 int EventEdit::delete_type_in_range_pred(mevent
* e
){
618 if(e
->tick
> delete_t1
&& e
->tick
< delete_t2
&& match_event_type(e
))
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
){
649 void EventEdit::get_event_color(mevent
* e
, fltk::Color
* c1
, fltk::Color
* c2
, fltk::Color
* c3
){
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);
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);
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);
696 *c1
= fltk::color(255,248,47);
697 *c2
= fltk::color(140,137,46);
698 *c3
= fltk::color(232,255,37);
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
){
710 if(M
>MAG_MAX
){M
=MAG_MAX
;}
714 case MIDI_AFTERTOUCH
:
718 case MIDI_CONTROLLER_CHANGE
:
719 *v1
= controller_type
;
722 case MIDI_PROGRAM_CHANGE
:
723 case MIDI_CHANNEL_PRESSURE
:
726 case MIDI_PITCH_WHEEL
:
728 *v2
= (M
&0x3f80) >> 7;
733 int EventEdit::xpix2tick(int xpix
){
734 ui
->piano_roll
->xpix2tick(xpix
+scroll
);
737 int EventEdit::get_event_mag(mevent
* e
){
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);
754 int EventEdit::delete_type_all_pred(mevent
* e
){
755 if(match_event_type(e
))
761 int EventEdit::delete_all_non_note_pred(mevent
* e
){
762 if(e
->type
!= MIDI_NOTE_ON
&& e
->type
!= MIDI_NOTE_OFF
)
768 int EventEdit::delete_all_pred(mevent
* e
){
772 void EventEdit::clear_events(){
773 delete_events(&EventEdit::delete_type_all_pred
);
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;
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
);
791 ui
->piano_roll
->redraw();
792 for(int i
=2;i
<134;i
++){
795 ui
->event_menu
->redraw();
798 void EventEdit::clear_all_events(){
799 delete_events(&EventEdit::delete_all_pred
);
801 ui
->piano_roll
->redraw();
802 for(int i
=0;i
<134;i
++){
805 ui
->event_menu
->redraw();
808 void EventEdit::clear_selected_events(){
812 void EventEdit::clear_selection(){
814 mevent
* e
= cur_seqpat
->p
->events
->next
;
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
;
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;