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
24 #include <fltk/Group.h>
25 #include <fltk/Widget.h>
26 #include <fltk/events.h>
39 extern std::vector
<track
*> tracks
;
41 extern struct conf config
;
45 #define SWAP(X,Y) tmp=X; X=Y; Y=tmp;
47 PianoRoll::PianoRoll(int x
, int y
, int w
, int h
, const char* label
= 0) : fltk::Widget(x
, y
, w
, h
, label
) {
67 resize_handle_width
= 4;
71 if(fakeh
< h
){fakeh
= h
;}
74 int PianoRoll::handle(int event
){
88 if(event_key()==fltk::DeleteKey
){
93 ui
->event_edit
->redraw();
96 if(event_state(CTRL
) && event_key()=='c'){
97 //printf("roll copy\n");
100 if(zoom_out_key(event_key(),event_state())){
103 set_zoom(30*(1<<zoom_n
)/16);
104 ui
->pattern_timeline
->zoom
= zoom
;
105 ui
->pattern_timeline
->update(get_play_position());
106 ui
->pattern_timeline
->redraw();
107 ui
->event_edit
->zoom
= zoom
;
108 ui
->event_edit
->redraw();
113 if(zoom_in_key(event_key(),event_state())){
116 set_zoom(30*(1<<zoom_n
)/16);
117 ui
->pattern_timeline
->zoom
= zoom
;
118 ui
->pattern_timeline
->update(get_play_position());
119 ui
->pattern_timeline
->redraw();
120 ui
->event_edit
->zoom
= zoom
;
121 ui
->event_edit
->redraw();
130 if(event_button()==1){//left mouse
131 if(e
==NULL
){//new note init
132 if(event_state()&fltk::SHIFT
){//begin box
138 box_t1
=xpix2tick(X
+scrollx
);
140 box_n1
=ypix2note(Y
+scrolly
,1);
145 insert_torig
= quantize(xpix2tick(X
+scrollx
));
146 insert_toffset
= q_tick
;
147 //new_orig_t = new_left_t;
148 insert_note
= ypix2note(Y
+scrolly
,1);
150 last_note
= insert_note
;
151 if(config
.playinsert
){
152 ui
->keyboard
->play_note(last_note
,0);
159 if(!(e
->selected
) && !(event_state()&fltk::SHIFT
)){
163 resize_arrow_color
= fltk::color(128,128,0);
165 if(over_rhandle(e
)){//resize
167 rresize_torig
= e
->tick
+e
->dur
;
170 else if(over_lhandle(e
)){//resize move
172 lresize_torig
= e
->tick
;
178 move_torig
= e
->tick
;
179 move_qoffset
= e
->tick
- quantize(e
->tick
);
181 move_toffset
= -move_qoffset
;
183 //move_offset = quantize(xpix2tick(X)) - move_torig - move_qoffset;
185 move_offset
= X
- tick2xpix(e
->tick
);
186 move_norig
= ypix2note(Y
+scrolly
,1);
189 last_note
= move_norig
;
191 ui
->keyboard
->play_note(last_note
,0);
196 else if(event_button()==2){//middle mouse
197 //button initiates paste
199 else if(event_button()==3){//right mouse
203 ui
->event_edit
->redraw();
205 else{//set up for deletion
206 if(!(e
->selected
) && !(event_state()&fltk::SHIFT
)){
211 resize_arrow_color
= fltk::color(120,60,58);
222 box_t2
= xpix2tick(X
+scrollx
);
223 box_n2
= ypix2note(Y
+scrolly
,1);
225 else if(insert_flag
){
226 insert_toffset
= quantize(xpix2tick(X
+scrollx
)+q_tick
) - insert_torig
;
227 if(insert_toffset
<=0){
228 insert_toffset
-= q_tick
;
230 insert_note
= ypix2note(Y
+scrolly
,1);
231 if(insert_note
!= last_note
){
232 if(config
.playinsert
){//play on insert
233 ui
->keyboard
->release_note(last_note
,0);
234 ui
->keyboard
->play_note(insert_note
,0);
236 last_note
= insert_note
;
240 move_toffset
= quantize(xpix2tick(X
- move_offset
)) - move_torig
;
241 move_noffset
= ypix2note(Y
+scrolly
,1) - move_norig
;
242 int N
= move_norig
+move_noffset
;
244 if(config
.playmove
){//play on move
245 ui
->keyboard
->release_note(last_note
,0);
246 ui
->keyboard
->play_note(N
,0);
251 else if(rresize_flag
){
252 rresize_toffset
= quantize(xpix2tick(X
+scrollx
))+q_tick
-rresize_torig
;
254 else if(lresize_flag
){
255 lresize_toffset
= quantize(xpix2tick(X
+scrollx
)) - lresize_torig
;
261 if(event_button()==1){
264 ui
->event_edit
->redraw();
267 else if(rresize_flag
){
271 ui
->event_edit
->redraw();
273 else if(lresize_flag
){
277 ui
->event_edit
->redraw();
279 else if(insert_flag
){
284 ui
->keyboard
->release_note(insert_note
,0);
285 ui
->keyboard
->redraw();
286 ui
->event_edit
->has
[0]=1;
287 ui
->event_edit
->has
[1]=1;
288 ui
->event_edit
->redraw();
289 ui
->event_menu
->redraw();
295 midi_track_off(cur_seqpat
->track
);
296 ui
->keyboard
->release_note(last_note
,0);
297 ui
->keyboard
->release_note(move_norig
+move_noffset
,0);
298 ui
->keyboard
->redraw();
299 ui
->event_edit
->redraw();
304 if(event_button()==3){
305 mevent
* over_n
= over_note();
306 if(delete_flag
&& over_n
){
307 if(over_n
->selected
){
309 midi_track_off(cur_seqpat
->track
);
310 ui
->event_edit
->redraw();
324 if(resize_e
!= e
|| resize_arrow
!= 1){
325 if(e
->selected
){resize_arrow_color
= fltk::color(128,128,0);}
326 else{resize_arrow_color
= fltk::color(95,58,119);}
329 resize_x
= tick2xpix(e
->tick
+ e
->dur
)-scrollx
-resize_handle_width
;
330 resize_y
= note2ypix(e
->value1
)-scrolly
;
334 else if(over_lhandle(e
)){
335 if(resize_e
!= e
|| resize_arrow
!= 1){
336 if(e
->selected
){resize_arrow_color
= fltk::color(128,128,0);}
337 else{resize_arrow_color
= fltk::color(95,58,119);}
340 resize_x
= tick2xpix(e
->tick
)+1 - scrollx
;
341 resize_y
= note2ypix(e
->value1
) - scrolly
;
346 if(resize_e
!= e
|| resize_arrow
!= 0){
354 if(resize_arrow
!= 0){
365 void PianoRoll::draw(){
367 fltk::push_clip(0,0,w(),h());
369 fltk::setcolor(fltk::GRAY05
);
370 fltk::fillrect(0,0,w(),h());
372 fltk::setcolor(fltk::GRAY20
);
373 for(int i
=12-scrolly
; i
<h(); i
+=12){
375 fltk::fillrect(0,i
,w(),1);
378 for(int i
=zoom
-scrollx
; i
<w(); i
+=zoom
){
380 fltk::fillrect(i
,0,1,h());
384 fltk::setcolor(fltk::GRAY30
);
385 for(int i
=12*5-scrolly
; i
<h(); i
+=12*7){
387 fltk::fillrect(0,i
,w(),1);
391 fltk::setcolor(fltk::GRAY50
);
392 for(int i
=zoom
*4-scrollx
; i
<w(); i
+=zoom
*4){
394 fltk::fillrect(i
,0,1,h());
398 fltk::setcolor(fltk::WHITE
);
399 int M
= config
.beats_per_measure
;
400 for(int i
=zoom
*4*M
-scrollx
; i
<w(); i
+=zoom
*4*M
){
402 fltk::fillrect(i
,0,1,h());
406 fltk::setcolor(fltk::color(128,0,0));
407 int rightend
= tick2xpix(cur_seqpat
->dur
)-scrollx
;
408 if(rightend
>=0 && rightend
< w()){
409 fltk::fillrect(rightend
,0,1,h());
412 fltk::setcolor(fltk::color(128,128,0));
413 fltk::fillrect(0,12*40-scrolly
,w(),1);
417 fltk::setcolor(fltk::BLUE
);
418 int T1
= insert_torig
;
419 int T2
= T1
+ insert_toffset
;
420 if(T1
>T2
){SWAP(T1
,T2
);}
421 int X
= tick2xpix(T1
)+1 - scrollx
;
422 int Y
= note2ypix(insert_note
) - scrolly
;
423 int W
= tick2xpix(T2
)-scrollx
- X
;
424 fltk::fillrect(X
,Y
,W
,11);
428 fltk::setcolor(fltk::MAGENTA
);
429 mevent
* ptr
= cur_seqpat
->p
->events
->next
;
431 if(ptr
->type
== MIDI_NOTE_ON
&& ptr
->selected
){
432 int X
= tick2xpix(ptr
->tick
+move_toffset
)+1-scrollx
;
433 int Y
= note2ypix(ptr
->value1
+move_noffset
)-scrolly
;
434 int W
= tick2xpix(ptr
->dur
);
435 fltk::fillrect(X
,Y
,W
-1,1);
436 fltk::fillrect(X
,Y
+11,W
-1,1);
437 fltk::fillrect(X
,Y
,1,11);
438 fltk::fillrect(X
+W
-2,Y
,1,11);
447 mevent
* e
= cur_seqpat
->p
->events
->next
;
449 fltk::Color c1
,c2
,c3
;
452 if(e
->type
== MIDI_NOTE_ON
){
453 //fltk::fillrect(tick2xpix(e->tick),note2ypix(e->value),e->dur,11);
455 int R1
= rresize_flag
&&e
->selected
? rresize_toffset
: 0;
456 int R2
= lresize_flag
&&e
->selected
? lresize_toffset
: 0;
458 int T1
= e
->tick
+ R2
;
459 int T2
= e
->tick
+e
->dur
+ R1
;
461 if(T1
>= T2
-q_tick
&& e
->selected
){
466 else if(lresize_flag
){
467 T2
= e
->tick
+ e
->dur
;
472 int X
= tick2xpix(T1
) + 1 - scrollx
;
473 int Y
= note2ypix(e
->value1
) - scrolly
;
475 int W
= tick2xpix(T2
)-scrollx
- X
;
476 get_event_color(e
,&c1
,&c2
,&c3
);
479 fltk::fillrect(X
+1,Y
+1,W
-1,10);
482 fltk::fillrect(X
,Y
+11,W
,1);
483 fltk::fillrect(X
+W
-1,Y
+1,1,11);
486 fltk::fillrect(X
,Y
,W
,1);
487 fltk::fillrect(X
,Y
,1,11);
493 if(!rresize_flag
&& !lresize_flag
){
494 if(resize_arrow
> 0){
495 setcolor(resize_arrow_color
);
497 int W
= resize_handle_width
;
504 addvertex(X
+W
,Y
+H
/2);
507 else if(resize_arrow
< 0){
508 setcolor(resize_arrow_color
);
510 int W
= resize_handle_width
;
524 fltk::setcolor(fltk::GREEN
);
542 fltk::fillrect(X1
,Y1
,X2
-X1
,1);
543 fltk::fillrect(X1
,Y1
,1,Y2
-Y1
);
544 fltk::fillrect(X2
,Y1
,1,Y2
-Y1
);
545 fltk::fillrect(X1
,Y2
,X2
-X1
,1);
554 void PianoRoll::scrollTo(int X
, int Y
){
558 ui
->pattern_timeline
->scroll
= X
;
559 ui
->pattern_timeline
->redraw();
560 ui
->event_edit
->scroll
= X
;
561 ui
->event_edit
->redraw();
562 ui
->keyboard
->scroll
= Y
;
563 ui
->keyboard
->redraw();
568 void PianoRoll::load(seqpat
* s
){
570 //ui->pattern_scroll->scrollTo(0,300);
573 cur_track
= tracks
[s
->track
];
574 int W
= tick2xpix(s
->dur
);
577 ui
->pattern_timeline
->ticks_offset
= s
->tick
;
582 int PianoRoll::note2ypix(int note
){
583 int udy
= 6*(note
+ (note
+7)/12 + note
/12) + 12;
584 return 900 - udy
+ 1;
587 int PianoRoll::tick2xpix(int tick
){
588 return tick
*zoom
*4 / 128;
591 int PianoRoll::xpix2tick(int xpix
){
592 return xpix
*128 / (zoom
*4);
595 int PianoRoll::quantize(int tick
){
596 return tick
/q_tick
* q_tick
;
600 void PianoRoll::set_zoom(int z
){
603 //int W = tick2xpix(cur_seqpat->dur);
608 mevent
* PianoRoll::over_note(){
609 mevent
* e
= cur_seqpat
->p
->events
->next
;
611 int X
= event_x()+scrollx
;
612 int Y
= event_y()+scrolly
;
616 if(e
->type
== MIDI_NOTE_ON
){
617 cy
= note2ypix(e
->value1
);
618 lx
= tick2xpix(e
->tick
);
619 rx
= tick2xpix(e
->tick
+e
->dur
);
620 if(X
> lx
&& X
< rx
&&
621 Y
< cy
+12 && Y
> cy
){
633 void PianoRoll::update(int pos
){
634 if(!is_backend_playing() || !cur_seqpat
){
637 int X1
= tick2xpix(pos
-cur_seqpat
->tick
);
638 int X2
= X1
- scrollx
;
640 scrollTo(X1
-50<0?0:X1
-50,scrolly
);
643 scrollTo(X1
-50,scrolly
);
648 void PianoRoll::unselect_all(){
649 mevent
* e
= cur_seqpat
->p
->events
;
651 if(e
->type
== MIDI_NOTE_ON
&& e
->selected
==1){
660 void PianoRoll::get_event_color(mevent
* e
, fltk::Color
* c1
, fltk::Color
* c2
, fltk::Color
* c3
){
666 *c1
= fltk::color(229,79,75);
667 *c2
= fltk::color(120,60,58);
668 *c3
= fltk::color(225,131,109);
679 if(T1
>T2
){SWAP(T1
,T2
);}
680 if(N1
<N2
){SWAP(N1
,N2
);}
681 if(e
->tick
+e
->dur
> T1
&& e
->tick
< T2
&& N
>= N2
&& N
<= N1
){
682 *c1
= fltk::color(108,229,75);
683 *c2
= fltk::color(71,120,59);
684 *c3
= fltk::color(108,229,75);
690 *c1
= fltk::color(255,248,47);
691 *c2
= fltk::color(140,137,46);
692 *c3
= fltk::color(232,255,37);
696 *c1
= fltk::color(169,75,229);
697 *c2
= fltk::color(95,58,119);
698 *c3
= fltk::color(198,109,225);
702 void PianoRoll::apply_box(){
703 mevent
* e
= cur_seqpat
->p
->events
->next
;
710 if(T1
>T2
){SWAP(T1
,T2
);}
711 if(N1
<N2
){SWAP(N1
,N2
);}
714 if(e
->type
== MIDI_NOTE_ON
&&
715 e
->tick
+e
->dur
> T1
&& e
->tick
< T2
&&
723 void PianoRoll::apply_insert(){
724 if(insert_note
> 127 || insert_note
< 0){
729 int T1
= insert_torig
;
730 int T2
= T1
+ insert_toffset
;
731 if(T1
>T2
){SWAP(T1
,T2
);}
737 pattern
* p
= cur_seqpat
->p
;
738 Command
* c
=new CreateNote(p
,insert_note
,config
.defaultvelocity
,T1
,T2
-T1
);
742 cur_track
->restate();
745 void PianoRoll::apply_delete(){
749 pattern
* p
= cur_seqpat
->p
;
752 e
= cur_seqpat
->p
->events
->next
;
755 if(e
->selected
&& e
->type
== MIDI_NOTE_ON
){
756 c
=new DeleteNote(p
,e
);
764 cur_track
->restate();
767 void PianoRoll::apply_move(){
768 if(move_toffset
==0 && move_noffset
==0){
772 pattern
* p
= cur_seqpat
->p
;
773 mevent
* e
= p
->events
->next
;
775 int K
= e
->value1
+move_noffset
;
776 int T
= e
->tick
+move_toffset
;
777 if(e
->type
== MIDI_NOTE_ON
&& e
->selected
&& (T
<0 || K
< 0 || K
> 127)){
789 for(int i
=0; i
<tracks
.size(); i
++){
793 if(e
->selected
&& e
->modified
== 0){
794 int K
= e
->value1
+ move_noffset
;
795 int T
= e
->tick
+ move_toffset
;
797 c
=new MoveNote(p
,e
,T
,K
);
808 if(e
->modified
){e
->modified
=0;}
812 cur_track
->restate();
815 void PianoRoll::apply_paste(){
821 void PianoRoll::apply_rresize(){
822 if(rresize_toffset
==0){
829 pattern
* p
= cur_seqpat
->p
;
836 if(e
->type
== MIDI_NOTE_ON
&& e
->selected
&& e
->modified
== 0){
839 int R
= rresize_toffset
;
843 c
=new ResizeNote(p
,e
,W
+R
);
852 if(e
->modified
){e
->modified
=0;}
856 cur_track
->restate();
860 void PianoRoll::apply_lresize(){
861 if(lresize_toffset
==0){
868 pattern
* p
= cur_seqpat
->p
;
874 if(e
->type
== MIDI_NOTE_ON
&& e
->selected
){
875 if(e
->tick
+ lresize_toffset
< 0){
885 if(e
->type
== MIDI_NOTE_ON
&& e
->selected
&& e
->modified
== 0){
889 int R
= lresize_toffset
;
895 mevent
* etmp
= e
->prev
;
896 c
=new ResizeNote(p
,e
,W
-R
);
900 c
=new MoveNote(p
,e
,T
+R
,e
->value1
);
910 if(e
->modified
){e
->modified
=0;}
914 cur_track
->restate();
926 int PianoRoll::over_rhandle(mevent
* e
){
927 int X
= event_x()+scrollx
;
928 int Y
= event_y()+scrolly
;
929 int X1
= tick2xpix(e
->tick
);
930 int X2
= X1
+ tick2xpix(e
->dur
);
931 int Y1
= note2ypix(e
->value1
);
934 if(X2
-X1
< resize_handle_width
*3){
938 return (Y
> Y1
&& Y
< Y2
&& X
< X2
&& X
> X2
- resize_handle_width
);
941 int PianoRoll::over_lhandle(mevent
* e
){
942 int X
= event_x()+scrollx
;
943 int Y
= event_y()+scrolly
;
944 int X1
= tick2xpix(e
->tick
);
945 int X2
= X1
+ tick2xpix(e
->dur
);
946 int Y1
= note2ypix(e
->value1
);
949 if(X2
-X1
< resize_handle_width
*3){
953 return (Y
> Y1
&& Y
< Y2
&& X
< X1
+ resize_handle_width
+1 && X
> X1
+ 1);