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
25 #include <fltk/Group.h>
26 #include <fltk/Widget.h>
27 #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 Arranger::Arranger(int x
, int y
, int w
, int h
, const char* label
= 0) : fltk::Widget(x
, y
, w
, h
, label
) {
48 new_default_w
= 128*4;
75 int Arranger::handle(int event
){
91 case fltk::MOUSEWHEEL
:
98 else if(event_dy()<0){
106 if(event_state() && event_key()=='c'){
110 if(event_state() && event_key()=='v'){
114 if(event_state() && event_key()=='z'){
118 if(event_key()==fltk::DeleteKey
){
124 if(zoom_out_key(event_key(),event_state())){
125 //if(event_key()==fltk::LeftKey){
128 zoom
= 30*(1<<zoom_n
)/16;
129 ui
->song_timeline
->zoom
= 30*(1<<zoom_n
)/16;
130 ui
->song_timeline
->update(get_play_position());
131 ui
->song_timeline
->redraw();
137 if(zoom_in_key(event_key(),event_state())){
138 //if(event_key()==fltk::RightKey){
141 zoom
= 30*(1<<zoom_n
)/16;
142 ui
->song_timeline
->zoom
= 30*(1<<zoom_n
)/16;
143 ui
->song_timeline
->update(get_play_position());
144 ui
->song_timeline
->redraw();
153 if(event_button()==1){//left mouse
154 seqpat
* s
= over_seqpat();
156 if(color_flag
){//do nothing
158 else if(event_state()&fltk::SHIFT
){//begin box
171 insert_torig
= xpix2tick(X
)/q_tick
*q_tick
;
172 insert_toffset
= q_tick
;
173 insert_track
= event_y() / 30;
180 color_orig_x
= event_x();
181 color_orig_y
= event_y();
182 color_orig_h
= color_sel
->h
;
183 color_orig_v
= color_sel
->v
;
184 color_h
= color_orig_h
;
185 color_v
= color_orig_v
;
188 if(!s
->selected
&& !(event_state()&SHIFT
)){
192 if(fltk::event_clicks() > 0){//'double click'
193 ui
->piano_roll
->load(s
);
194 ui
->event_edit
->load(s
);
195 ui
->pattern_scroll
->scrollTo(s
->scrollx
,s
->scrolly
);
196 ui
->pattern_timeline
->update(get_play_position());
197 ui
->keyboard
->cur_port
= tracks
[s
->track
]->port
;
198 ui
->keyboard
->cur_chan
= tracks
[s
->track
]->chan
;
199 ui
->track_info
->set_rec(s
->track
);
200 set_rec_track(s
->track
);
205 if(over_lhandle(s
,X
,Y
)){//begin resize
207 lresize_torig
= s
->tick
;
210 else if(over_rhandle(s
,X
,Y
)){//begin resizemove
212 rresize_torig
= s
->tick
+s
->dur
;
218 move_torig
= s
->tick
;
220 move_korig
= s
->track
;
224 move_offset
= xpix2tick(X
)/q_tick
*q_tick
- s
->tick
;
228 else if(event_button()==2){//middle mouse
229 seqpat
* s
= over_seqpat();
233 s
->p
->regen_colors();
239 paste_t
= quantize(xpix2tick(event_x()));
240 paste_track
= event_y() / 30;
243 else if(event_button()==3){//right mouse
244 seqpat
* s
= over_seqpat();
246 seqpat
* ptr
= tracks
[s
->track
]->head
->next
;
250 ptr
->p
->regen_colors();
264 delete_sel
= s
;//this line needs to be removed
277 box_t2
= xpix2tick(X
);
280 if(color_flag
&& color_sel
){
281 color_sel
->h
= color_orig_h
+ (color_orig_x
- event_x())/1.0;
282 color_sel
->v
= color_orig_v
+ (color_orig_y
- event_y())/100.0;
283 color_sel
->regen_colors();
284 color_h
= color_sel
->h
;
285 color_v
= color_sel
->v
;
286 set_default_hsv_value(color_v
);
289 insert_toffset
= xpix2tick(X
)/q_tick
*q_tick
+ q_tick
- insert_torig
;
290 if(insert_toffset
<=0){
291 insert_toffset
-= q_tick
;
293 insert_track
= Y
/ 30;
295 else if(rresize_flag
){
296 rresize_toffset
= xpix2tick(X
)/128*128 - rresize_torig
;
298 else if(lresize_flag
){
299 lresize_toffset
= xpix2tick(X
)/128*128 - lresize_torig
;
302 move_toffset
= quantize(xpix2tick(X
)) - move_torig
- move_offset
;
303 move_koffset
= event_y() / 30 - move_korig
;
306 paste_t
= quantize(xpix2tick(event_x()));
307 paste_track
= event_y() / 30;
312 if(event_button()==1){
325 else if(rresize_flag
){
329 last_handle
->lhandle
= 0;
330 last_handle
->rhandle
= 0;
333 else if(lresize_flag
){
337 last_handle
->lhandle
= 0;
338 last_handle
->rhandle
= 0;
345 else if(event_button()==2){
351 else if(event_button()==3){
352 seqpat
* over_s
= over_seqpat();
353 if(delete_flag
&& over_s
){
354 if(over_s
->selected
){
367 if(color_flag
){break;}
368 seqpat
* s
= over_seqpat();
370 if(over_rhandle(s
,X
,Y
)){s
->rhandle
= 1;}
371 else{s
->rhandle
= 0;}
372 if(over_lhandle(s
,X
,Y
)){s
->lhandle
= 1;}
373 else{s
->lhandle
= 0;}
374 if(s
!= last_handle
){
376 last_handle
->rhandle
= 0;
377 last_handle
->lhandle
= 0;
383 else if(last_handle
){
384 last_handle
->rhandle
= 0;
385 last_handle
->lhandle
= 0;
395 void Arranger::draw(){
397 fltk::setfont(fltk::HELVETICA
,8);
399 fltk::setcolor(fltk::GRAY05
);
400 fltk::fillrect(0,0,w(),h());
402 fltk::setcolor(fltk::GRAY20
);
403 int M
= config
.beats_per_measure
;
405 for(int i
=1; I
<w(); i
++){
407 fltk::fillrect(I
,0,1,h());
409 fltk::setcolor(fltk::GRAY50
);
410 int P
= config
.measures_per_phrase
;
413 for(int i
=1; I
<w(); i
++){
414 I
= i
*zoom
*4*P
*M
/4/4;
415 fltk::fillrect(I
,0,1,h());
421 fltk::setcolor(fltk::BLUE
);
422 int T1
= insert_torig
;
423 int T2
= T1
+ insert_toffset
;
425 if(T1
>T2
){SWAP(T1
,T2
);}
426 int X
= tick2xpix(T1
)+1;
427 int Y
= insert_track
*30;
428 int W
= tick2xpix(T2
)-tick2xpix(T1
) - 1;
429 fltk::fillrect(X
,Y
,W
,28);
433 if(check_move_safety()){
434 fltk::setcolor(fltk::MAGENTA
);
437 fltk::setcolor(fltk::RED
);
440 for(int i
=0; i
<tracks
.size(); i
++){
441 seqpat
* s
= tracks
[i
]->head
->next
;
444 int X
= tick2xpix(s
->tick
+ move_toffset
);
445 int Y
= (s
->track
+ move_koffset
)*30;
446 int W
= tick2xpix(s
->dur
);
447 fltk::fillrect(X
+1,Y
+1,W
-1,1);
448 fltk::fillrect(X
+1,Y
+1,1,29-1);
449 fltk::fillrect(X
+1,Y
+29-1,W
-1,1);
450 fltk::fillrect(X
+W
-1,Y
+1,1,29-1);
458 fltk::setcolor(fltk::GREEN
);
459 int X
= tick2xpix(paste_t
)+1;
460 int Y
= paste_track
*zoom
;
461 int W
= tick2xpix(main_sel
->dur
);
462 fltk::fillrect(X
,Y
,W
-1,1);
463 fltk::fillrect(X
,Y
+28,W
-1,1);
464 fltk::fillrect(X
,Y
,1,28);
465 fltk::fillrect(X
+W
-2,Y
,1,28);
470 fltk::setcolor(fltk::GREEN
);
476 if(X1
>X2
){SWAP(X1
,X2
);}
477 if(Y1
>Y2
){SWAP(Y1
,Y2
);}
478 fltk::fillrect(X1
,Y1
,X2
-X1
,1);
479 fltk::fillrect(X1
,Y1
,1,Y2
-Y1
);
480 fltk::fillrect(X2
,Y1
,1,Y2
-Y1
);
481 fltk::fillrect(X1
,Y2
,X2
-X1
,1);
488 fltk::Color c1
,c2
,c3
,cx
;
491 for(int i
=0; i
<tracks
.size(); i
++){
493 s
= tracks
[i
]->head
->next
;
498 get_outline_color(s
,&c1
,&c2
,&c3
,&cx
);
502 int R1
= lresize_flag
&&s
->selected
? lresize_toffset
: 0;
503 int R2
= rresize_flag
&&s
->selected
? rresize_toffset
: 0;
506 int T2
= s
->tick
+s
->dur
+R2
;
508 if(T1
> T2
){SWAP(T1
,T2
)};
510 int X
= tick2xpix(T1
)+1;
511 int Y
= s
->track
* 30;
512 int W
= tick2xpix(T2
)-tick2xpix(T1
)-1;
514 if(rresize_flag
&& s
->selected
&& T1
==T2
){
515 W
= tick2xpix(128)-1;
517 if(lresize_flag
&& s
->selected
&& T1
==T2
){
518 W
= tick2xpix(128)-1;
521 fillrect(X
+1,Y
+1,W
-2,27);
526 fillrect(X
+W
-1,Y
,1,29);
527 fillrect(X
,Y
+28,W
-1,1);
533 fltk::push_clip(tick2xpix(T1
),s
->track
*30,tick2xpix(T2
-T1
),30);
535 if(s
->rhandle
&& !rresize_flag
){
538 setcolor(fltk::color(128,0,0));
542 X
= tick2xpix(s
->tick
+s
->dur
) - W
- 1;
544 addvertex(X
+W
,Y
+28/2);
550 if(s
->lhandle
&& !lresize_flag
){
553 setcolor(fltk::color(128,0,0));
556 X
= tick2xpix(s
->tick
)+1;
567 mevent
* e
= s
->p
->events
;
569 if(e
->tick
>= s
->dur
){
572 if(e
->type
== MIDI_NOTE_ON
){
573 X
= tick2xpix(e
->tick
) + tick2xpix(s
->tick
)+2;
574 Y
= s
->track
*30 + 27 - e
->value1
*27/127;
575 W
= tick2xpix(e
->dur
);
583 int total
= s
->layer_total();
585 fltk::setcolor(fltk::BLACK
);
586 int X
= tick2xpix(s
->tick
);
587 int Y
= s
->track
* 30;
588 int count
= s
->layer_index()+1;
590 snprintf(buf
,16,"%d / %d",count
,total
);
591 fltk::drawtext(buf
,X
+2,Y
+27);
602 static int kludge
= 4;//see the same kludge in pianoroll.cpp
603 void Arranger::layout(){
610 for(int i
=0; i
<tracks
.size(); i
++){
611 seqpat
* s
= tracks
[i
]->head
->next
;
613 if(s
->tick
+s
->dur
> maxt
){maxt
=s
->tick
+s
->dur
;}
617 int ws
= tick2xpix(maxt
);
625 int wp
= ui
->song_scroll
->w();
630 int hp
= ui
->song_scroll
->h();
638 int xp
= ui
->song_scroll
->xposition();
639 int yp
= ui
->song_scroll
->yposition();
640 ui
->song_timeline
->scroll
= xp
;
641 ui
->track_info
->scroll
= yp
;
644 ui
->song_timeline
->redraw();
647 ui
->track_info
->redraw();
657 seqpat
* Arranger::over_seqpat(){
658 int track
= event_y() / 30;
659 if(track
>= tracks
.size()){
662 int tick
= xpix2tick(event_x());
663 seqpat
* s
= tfind
<seqpat
>(tracks
[track
]->head
,tick
);
665 if(tick
< s
->tick
+s
->dur
){
673 //true if over right handle of s
674 int Arranger::over_rhandle(seqpat
* s
, int X
, int Y
){
675 int X1
= tick2xpix(s
->tick
);
676 int X2
= X1
+ tick2xpix(s
->dur
);
677 int Y1
= s
->track
* 30 + 1;
680 if(tick2xpix(s
->dur
) < 10){
684 return (Y
> Y1
&& Y
< Y2
&& X
< X2
&& X
> X2
- 5);
687 //true if over left handle of s
688 int Arranger::over_lhandle(seqpat
* s
, int X
, int Y
){
689 int X1
= tick2xpix(s
->tick
);
690 int X2
= X1
+ tick2xpix(s
->dur
);
691 int Y1
= s
->track
* 30 + 1;
694 if(tick2xpix(s
->dur
) < 10){
698 return (Y
> Y1
&& Y
< Y2
&& X
< X1
+ 5 + 1 && X
> X1
+1);
701 // 4=beats per measure, 128=ticks per beat, 30=width of measure in pixels
702 int Arranger::tick2xpix(int tick
){
703 return tick
*zoom
/(128*4);
706 int Arranger::xpix2tick(int xpix
){
707 return xpix
* (128*4) /zoom
;
710 int Arranger::quantize(int tick
){
711 return tick
/q_tick
* q_tick
;
715 void Arranger::update(int pos
){
716 if(!is_backend_playing()){
719 int wp
= ui
->song_scroll
->w();
720 int xp
= ui
->song_scroll
->xposition();
721 int yp
= ui
->song_scroll
->yposition();
722 int X1
= tick2xpix(pos
);
728 ui
->song_scroll
->scrollTo(X1
-50<0?0:X1
-50,yp
);
731 ui
->song_scroll
->scrollTo(X1
-50,yp
);
736 void Arranger::unselect_all(){
738 for(int i
=0; i
<tracks
.size(); i
++){
739 s
= tracks
[i
]->head
->next
;
749 void Arranger::get_outline_color(seqpat
* s
, fltk::Color
* c1
, fltk::Color
* c2
, fltk::Color
* c3
, fltk::Color
* cx
){
752 *c1
= fltk::color(p
->r1
, p
->g1
, p
->b1
);
753 *cx
= fltk::color(p
->rx
, p
->gx
, p
->bx
);
757 seqpat
* over_s
= over_seqpat();
758 if(delete_flag
&& s
->selected
){
759 *c1
= fltk::color(255,0,0);
760 *c2
= fltk::color(255,0,0);
761 *c3
= fltk::color(255,0,0);
771 if(T1
>T2
){SWAP(T1
,T2
);}
772 if(K1
<K2
){SWAP(K1
,K2
);}
773 if(s
->tick
+s
->dur
> T1
&& s
->tick
< T2
&& K
>= K2
&& K
<= K1
){
774 *c1
= fltk::color(0,255,0);
775 *c2
= fltk::color(71,120,59);
776 *c3
= fltk::color(108,229,75);
782 *c1
= fltk::color(255,255,0);
783 *c2
= fltk::color(140,137,46);
784 *c3
= fltk::color(232,255,37);
785 *cx
= fltk::color(128,128,0);
790 *c2
= fltk::color(p
->r2
,p
->g2
,p
->b2
);
791 *c3
= fltk::color(p
->r3
,p
->g3
,p
->b3
);
796 void Arranger::apply_insert(){
798 if(!check_insert_safety()){
803 int T1
= insert_torig
;
804 int T2
= T1
+ insert_toffset
;
805 if(T1
>T2
){SWAP(T1
,T2
);}
807 Command
* c
=new CreateSeqpatBlank(insert_track
,T1
,T2
-T1
);
811 if(T2
>maxt
){relayout();}
814 void Arranger::apply_box(){
821 if(T1
>T2
){SWAP(T1
,T2
);}
822 if(K1
>K2
){SWAP(K1
,K2
);}
824 if(K2
> tracks
.size()-1){K2
= tracks
.size()-1;}
825 for(int i
=K1
; i
<=K2
; i
++){
826 s
= tracks
[i
]->head
->next
;
828 if(s
->tick
+s
->dur
> T1
&& s
->tick
< T2
){
837 void Arranger::apply_delete(){
842 for(int i
=0; i
<tracks
.size(); i
++){
843 s
= tracks
[i
]->head
->next
;
847 tracks
[s
->track
]->modified
= 1;
848 c
=new DeleteSeqpat(s
);
857 unmodify_and_unstick_tracks();
861 void Arranger::apply_move(){
862 if(move_toffset
==0 && move_koffset
==0){
866 if(!check_move_safety()){
874 for(int i
=0; i
<tracks
.size(); i
++){
875 s
= tracks
[i
]->head
->next
;
878 if(s
->selected
&& s
->modified
== 0){
879 int K
= s
->track
+ move_koffset
;
880 int T
= s
->tick
+ move_toffset
;
881 tracks
[s
->track
]->modified
= 1;
882 tracks
[K
]->modified
= 1;
884 c
=new MoveSeqpat(s
,K
,T
);
888 if(T
+s
->dur
> maxt
){relayout();}
896 unmodify_and_unstick_tracks();
899 void Arranger::apply_paste(){
905 c
= new CreateSeqpat(paste_track
,paste_t
,main_sel
,0);
912 void Arranger::apply_rresize(){
913 if(rresize_toffset
==0){
917 if(!check_resize_safety()){
926 for(int i
=0; i
<tracks
.size(); i
++){
927 s
= tracks
[i
]->head
->next
;
930 if(s
->selected
&& s
->modified
== 0){
931 tracks
[i
]->modified
= 1;
934 int T2
= s
->tick
+ s
->dur
+ rresize_toffset
;
937 seqpat
* stmp
= s
->prev
;
938 //c=new ReverseSeqpat(s);
941 c
=new ResizeSeqpat(s
,T2
-T1
);
944 c
=new MoveSeqpat(s
,s
->track
,T1
);
949 if(s
->tick
+s
->dur
> maxt
){relayout();}
955 c
=new ResizeSeqpat(s
,T2
-T1
);
959 if(T2
> maxt
){relayout();}
969 unmodify_and_unstick_tracks();
972 void Arranger::apply_lresize(){
973 if(lresize_toffset
==0){
977 if(!check_resize_safety()){
986 for(int i
=0; i
<tracks
.size(); i
++){
987 s
= tracks
[i
]->head
->next
;
990 if(s
->selected
&& s
->modified
== 0){
991 tracks
[i
]->modified
= 1;
993 int T1
= s
->tick
+ lresize_toffset
;
994 int T2
= s
->tick
+ s
->dur
;
997 seqpat
* stmp
= s
->prev
;
998 //c=new ReverseSeqpat(s);
1001 c
=new ResizeSeqpat(s
,T2
-T1
);
1004 c
=new MoveSeqpat(s
,s
->track
,T1
);
1009 if(s
->tick
+s
->dur
> maxt
){relayout();}
1013 T2
= T1
+128; //magic
1015 seqpat
* stmp
= s
->prev
;
1016 c
=new MoveSeqpat(s
,s
->track
,T1
);
1019 c
=new ResizeSeqpat(s
,T2
-T1
);
1024 if(s
->tick
+s
->dur
>maxt
){relayout();}
1034 unmodify_and_unstick_tracks();
1039 int collision_test(int t11
, int t12
, int t21
, int t22
){
1040 return !((t11
< t21
&& t12
<= t21
) ||
1041 (t11
>= t22
&& t12
> t22
)) ? 1 : 0;
1044 int Arranger::check_move_safety(){
1048 for(int i
=0; i
<tracks
.size(); i
++){
1049 s
= tracks
[i
]->head
->next
;
1052 if(i
+move_koffset
< 0 || i
+move_koffset
> tracks
.size()-1 ||
1053 s
->tick
+ move_toffset
< 0){
1056 ptr
= tracks
[i
+move_koffset
]->head
->next
;
1059 ptr
=ptr
->next
; continue;
1061 if(collision_test(s
->tick
+move_toffset
,s
->tick
+s
->dur
+move_toffset
,ptr
->tick
,ptr
->tick
+ptr
->dur
) ){
1076 int Arranger::check_insert_safety(){
1079 int T1
= insert_torig
;
1080 int T2
= T1
+ insert_toffset
;
1083 if(T1
>T2
){SWAP(T1
,T2
);}
1088 if(insert_track
> tracks
.size()-1){
1091 if(tracks
[insert_track
]==NULL
){
1095 s
= tracks
[insert_track
]->head
->next
;
1098 if(collision_test(T1
,T2
,s
->tick
,s
->tick
+s
->dur
)){
1107 int Arranger::check_resize_safety(){
1115 for(int i
=0; i
<tracks
.size(); i
++){
1116 s
= tracks
[i
]->head
->next
;
1119 s
= s
->next
; continue;
1124 T2
= s
->tick
+ s
->dur
+ rresize_toffset
;
1126 else if(lresize_flag
){
1127 T1
= s
->tick
+ lresize_toffset
;
1128 T2
= s
->tick
+ s
->dur
;
1130 if(T1
>T2
){SWAP(T1
,T2
);}
1135 ptr
= tracks
[s
->track
]->head
->next
;
1138 ptr
=ptr
->next
; continue;
1142 S2
= ptr
->tick
+ ptr
->dur
;
1145 S2
+= rresize_toffset
;
1147 else if(lresize_flag
){
1148 S1
+= lresize_toffset
;
1152 if(collision_test(T1
,T2
,S1
,S2
)){
1166 int Arranger::check_paste_safety(){