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
26 #include <fltk/Group.h>
27 #include <fltk/Widget.h>
28 #include <fltk/events.h>
40 extern std::vector
<track
*> tracks
;
42 extern struct conf config
;
46 #define SWAP(X,Y) tmp=X; X=Y; Y=tmp;
48 Arranger::Arranger(int x
, int y
, int w
, int h
, const char* label
= 0) : fltk::Widget(x
, y
, w
, h
, label
) {
49 new_default_w
= 128*4;
71 if(fakeh
< h
){fakeh
= h
;}
79 resize_handle_width
= 4;
87 int Arranger::handle(int event
){
103 case fltk::MOUSEWHEEL
:
110 else if(event_dy()<0){
118 if(event_state() && event_key()=='c'){
122 if(event_state() && event_key()=='v'){
126 if(event_state() && event_key()=='z'){
130 if(event_key()==fltk::DeleteKey
){
136 if(zoom_out_key(event_key(),event_state())){
137 //if(event_key()==fltk::LeftKey){
140 zoom
= 30*(1<<zoom_n
)/16;
141 ui
->song_timeline
->zoom
= 30*(1<<zoom_n
)/16;
142 ui
->song_timeline
->update(get_play_position());
143 ui
->song_timeline
->redraw();
149 if(zoom_in_key(event_key(),event_state())){
150 //if(event_key()==fltk::RightKey){
153 zoom
= 30*(1<<zoom_n
)/16;
154 ui
->song_timeline
->zoom
= 30*(1<<zoom_n
)/16;
155 ui
->song_timeline
->update(get_play_position());
156 ui
->song_timeline
->redraw();
165 if(event_button()==1){//left mouse
166 seqpat
* s
= over_seqpat();
168 if(color_flag
|| unclone_flag
){//do nothing
170 else if(event_state()&fltk::SHIFT
){//begin box
176 box_t1
=xpix2tick(X
+scrollx
);
178 box_k1
=(Y
+scrolly
)/30;
183 insert_torig
= xpix2tick(X
+scrollx
)/q_tick
*q_tick
;
184 insert_toffset
= q_tick
;
185 insert_track
= (Y
+scrolly
) / 30;
194 color_orig_h
= color_sel
->h
;
195 color_orig_v
= color_sel
->v
;
196 color_h
= color_orig_h
;
197 color_v
= color_orig_v
;
203 if(!s
->selected
&& !(event_state()&SHIFT
)){
207 if(fltk::event_clicks() > 0){//'double click'
208 ui
->piano_roll
->load(s
);
209 ui
->event_edit
->load(s
);
210 ui
->pattern_timeline
->update(get_play_position());
211 ui
->keyboard
->cur_port
= tracks
[s
->track
]->port
;
212 ui
->keyboard
->cur_chan
= tracks
[s
->track
]->chan
;
213 ui
->track_info
->set_rec(s
->track
);
214 set_rec_track(s
->track
);
219 if(over_lhandle(s
)){//begin resize
221 lresize_torig
= s
->tick
;
224 else if(over_rhandle(s
)){//begin resizemove
226 rresize_torig
= s
->tick
+s
->dur
;
232 move_torig
= s
->tick
;
234 move_korig
= s
->track
;
238 move_offset
= xpix2tick(X
+scrollx
)/q_tick
*q_tick
- s
->tick
;
242 else if(event_button()==2){//middle mouse
243 seqpat
* s
= over_seqpat();
247 s
->p
->regen_colors();
253 paste_t
= quantize(xpix2tick(X
+scrollx
));
254 paste_track
= (Y
+scrolly
) / 30;
257 else if(event_button()==3){//right mouse
258 seqpat
* s
= over_seqpat();
260 seqpat
* ptr
= tracks
[s
->track
]->head
->next
;
264 ptr
->p
->regen_colors();
278 delete_sel
= s
;//this line needs to be removed
283 resize_arrow_color
= fltk::color(128,0,0);
292 box_t2
= xpix2tick(X
+scrollx
);
293 box_k2
= (Y
+scrolly
)/30;
295 if(color_flag
&& color_sel
){
296 color_sel
->h
= color_orig_h
+ (color_orig_x
- X
)/1.0;
297 color_sel
->v
= color_orig_v
+ (color_orig_y
- Y
)/100.0;
298 color_sel
->regen_colors();
299 color_h
= color_sel
->h
;
300 color_v
= color_sel
->v
;
301 set_default_hsv_value(color_v
);
304 insert_toffset
= xpix2tick(X
+scrollx
)/q_tick
*q_tick
+q_tick
-insert_torig
;
305 if(insert_toffset
<=0){
306 insert_toffset
-= q_tick
;
308 insert_track
= (Y
+scrolly
) / 30;
310 else if(rresize_flag
){
311 rresize_toffset
= xpix2tick(X
+scrollx
)/128*128 - rresize_torig
;
313 else if(lresize_flag
){
314 lresize_toffset
= xpix2tick(X
+scrollx
)/128*128 - lresize_torig
;
317 move_toffset
= quantize(xpix2tick(X
+scrollx
)) - move_torig
- move_offset
;
318 move_koffset
= (Y
+scrolly
) / 30 - move_korig
;
321 paste_t
= quantize(xpix2tick(X
+scrollx
));
322 paste_track
= (Y
+scrolly
) / 30;
327 if(event_button()==1){
340 else if(rresize_flag
){
345 else if(lresize_flag
){
354 else if(event_button()==2){
360 else if(event_button()==3){
361 seqpat
* over_s
= over_seqpat();
362 if(delete_flag
&& over_s
){
363 if(over_s
->selected
){
376 if(color_flag
){break;}
377 seqpat
* s
= over_seqpat();
380 if(resize_s
!= s
|| resize_arrow
!= 1){
381 if(s
->selected
){resize_arrow_color
= fltk::color(128,128,0);}
382 else{resize_arrow_color
= fltk::color(s
->p
->r2
,s
->p
->g2
,s
->p
->b2
);}
385 resize_x
= tick2xpix(s
->tick
+ s
->dur
)-scrollx
-resize_handle_width
-1;
386 resize_y
= s
->track
*30-scrolly
;
390 else if(over_lhandle(s
)){
391 if(resize_s
!= s
|| resize_arrow
!= 1){
392 if(s
->selected
){resize_arrow_color
= fltk::color(128,128,0);}
393 else{resize_arrow_color
= fltk::color(s
->p
->r2
,s
->p
->g2
,s
->p
->b2
);}
396 resize_x
= tick2xpix(s
->tick
)+1 - scrollx
;
397 resize_y
= s
->track
*30 - scrolly
;
402 if(resize_arrow
!= 0){
409 if(resize_arrow
!= 0){
420 void Arranger::draw(){
422 fltk::push_clip(0,0,w(),h());
424 fltk::setfont(fltk::HELVETICA
,8);
426 fltk::setcolor(fltk::GRAY05
);
427 fltk::fillrect(0,0,w(),h());
429 fltk::setcolor(fltk::GRAY20
);
430 int M
= config
.beats_per_measure
;
432 for(int i
=1; I
<w(); i
++){
433 I
= i
*zoom
*M
/4 - scrollx
;
435 fltk::fillrect(I
,0,1,h());
439 fltk::setcolor(fltk::GRAY50
);
440 int P
= config
.measures_per_phrase
;
443 for(int i
=1; I
<w(); i
++){
444 I
= i
*zoom
*4*P
*M
/4/4 - scrollx
;
446 fltk::fillrect(I
,0,1,h());
452 fltk::setcolor(fltk::BLUE
);
453 int T1
= insert_torig
;
454 int T2
= T1
+ insert_toffset
;
456 if(T1
>T2
){SWAP(T1
,T2
);}
457 int X
= tick2xpix(T1
)+1 - scrollx
;
458 int Y
= insert_track
*30 - scrolly
;
459 int W
= tick2xpix(T2
)-tick2xpix(T1
) - 1;
460 fltk::fillrect(X
,Y
,W
,28);
464 if(check_move_safety()){
465 fltk::setcolor(fltk::MAGENTA
);
468 fltk::setcolor(fltk::RED
);
471 for(int i
=0; i
<tracks
.size(); i
++){
472 seqpat
* s
= tracks
[i
]->head
->next
;
475 int X
= tick2xpix(s
->tick
+ move_toffset
) - scrollx
;
476 int Y
= (s
->track
+ move_koffset
)*30 - scrolly
;
477 int W
= tick2xpix(s
->dur
);
478 fltk::fillrect(X
+1,Y
+1,W
-1,1);
479 fltk::fillrect(X
+1,Y
+1,1,29-1);
480 fltk::fillrect(X
+1,Y
+29-1,W
-1,1);
481 fltk::fillrect(X
+W
-1,Y
+1,1,29-1);
489 fltk::setcolor(fltk::GREEN
);
490 int X
= tick2xpix(paste_t
)+1 - scrollx
;
491 int Y
= paste_track
*30 - scrolly
;
492 int W
= tick2xpix(main_sel
->dur
);
493 fltk::fillrect(X
,Y
,W
-1,1);
494 fltk::fillrect(X
,Y
+28,W
-1,1);
495 fltk::fillrect(X
,Y
,1,28);
496 fltk::fillrect(X
+W
-2,Y
,1,28);
501 fltk::setcolor(fltk::GREEN
);
507 if(X1
>X2
){SWAP(X1
,X2
);}
508 if(Y1
>Y2
){SWAP(Y1
,Y2
);}
509 fltk::fillrect(X1
,Y1
,X2
-X1
,1);
510 fltk::fillrect(X1
,Y1
,1,Y2
-Y1
);
511 fltk::fillrect(X2
,Y1
,1,Y2
-Y1
);
512 fltk::fillrect(X1
,Y2
,X2
-X1
,1);
519 fltk::Color c1
,c2
,c3
,cx
;
522 for(int i
=0; i
<tracks
.size(); i
++){
524 s
= tracks
[i
]->head
->next
;
529 get_outline_color(s
,&c1
,&c2
,&c3
,&cx
);
533 int R1
= lresize_flag
&&s
->selected
? lresize_toffset
: 0;
534 int R2
= rresize_flag
&&s
->selected
? rresize_toffset
: 0;
537 int T2
= s
->tick
+s
->dur
+R2
;
539 if(T1
> T2
){SWAP(T1
,T2
)};
541 int X
= tick2xpix(T1
)+1 - scrollx
;
542 int Y
= s
->track
* 30 - scrolly
;
543 int W
= tick2xpix(T2
)-tick2xpix(T1
)-1;
545 if(rresize_flag
&& s
->selected
&& T1
==T2
){
546 W
= tick2xpix(128)-1;
548 if(lresize_flag
&& s
->selected
&& T1
==T2
){
549 W
= tick2xpix(128)-1;
552 fillrect(X
+1,Y
+1,W
-2,27);
557 fillrect(X
+W
-1,Y
,1,29);
558 fillrect(X
,Y
+28,W
-1,1);
564 fltk::push_clip(X
,Y
,W
,30);
569 mevent
* e
= s
->p
->events
;
571 if(e
->tick
>= s
->dur
){
574 if(e
->type
== MIDI_NOTE_ON
){
575 int X2
= tick2xpix(e
->tick
) + tick2xpix(s
->tick
)+2 - scrollx
;
576 Y
= s
->track
*30 + 27 - e
->value1
*27/127 - scrolly
;
577 int W2
= tick2xpix(e
->dur
);
579 if(!(X2
+W2
<0 || X2
>X
+W
)){
587 int total
= s
->layer_total();
589 fltk::setcolor(fltk::BLACK
);
590 int X
= tick2xpix(s
->tick
) - scrollx
;
591 int Y
= s
->track
* 30 - scrolly
;
592 int count
= s
->layer_index()+1;
594 snprintf(buf
,16,"%d / %d",count
,total
);
595 fltk::drawtext(buf
,X
+2,Y
+27);
605 if(!rresize_flag
&& !lresize_flag
){
606 if(resize_arrow
> 0){
607 setcolor(resize_arrow_color
);
609 int W
= resize_handle_width
;
616 addvertex(X
+W
,Y
+H
/2);
619 else if(resize_arrow
< 0){
620 setcolor(resize_arrow_color
);
622 int W
= resize_handle_width
;
638 void Arranger::scrollTo(int X
, int Y
){
640 if(is_backend_playing() && config
.follow
){
641 int pos
= tick2xpix(get_play_position());
642 if(pos
< X
|| pos
> X
+ w() - scrollbuffer
- 30){
643 ui
->song_hscroll
->value(scrollx
);
649 ui
->song_hscroll
->value(scrollx
);
652 ui
->song_timeline
->scroll
= X
;
653 ui
->song_timeline
->redraw();
654 ui
->track_info
->scroll
= Y
;
655 ui
->track_info
->redraw();
660 seqpat
* Arranger::over_seqpat(){
661 int track
= (event_y()+scrolly
) / 30;
662 if(track
>= tracks
.size()){
665 int tick
= xpix2tick(event_x()+scrollx
);
666 seqpat
* s
= tfind
<seqpat
>(tracks
[track
]->head
,tick
);
668 if(tick
< s
->tick
+s
->dur
){
676 //true if over right handle of s
677 int Arranger::over_rhandle(seqpat
* s
){
680 int X1
= tick2xpix(s
->tick
) - scrollx
;
681 int X2
= X1
+ tick2xpix(s
->dur
);
682 int Y1
= s
->track
* 30 + 1 - scrolly
;
685 if(tick2xpix(s
->dur
) < 10){
689 return (Y
> Y1
&& Y
< Y2
&& X
< X2
&& X
> X2
- 5);
692 //true if over left handle of s
693 int Arranger::over_lhandle(seqpat
* s
){
696 int X1
= tick2xpix(s
->tick
) - scrollx
;
697 int X2
= X1
+ tick2xpix(s
->dur
);
698 int Y1
= s
->track
* 30 + 1 - scrolly
;
701 if(tick2xpix(s
->dur
) < 10){
705 if(Y
> Y1
&& Y
< Y2
&& X
< X1
+ 5 + 1 && X
> X1
+1){
706 //printf("success\n");
708 return (Y
> Y1
&& Y
< Y2
&& X
< X1
+ 5 + 1 && X
> X1
+1);
711 // 4=beats per measure, 128=ticks per beat, 30=width of measure in pixels
712 int Arranger::tick2xpix(int tick
){
713 return tick
*zoom
/(128*4);
716 int Arranger::xpix2tick(int xpix
){
717 return xpix
* (128*4) /zoom
;
720 int Arranger::quantize(int tick
){
721 return tick
/q_tick
* q_tick
;
725 void Arranger::update(int pos
){
726 if(!is_backend_playing()){
729 int X1
= tick2xpix(pos
);
730 int X2
= X1
- scrollx
;
732 int target
= X1
-50<0?0:X1
-50;
733 scrollTo(target
,scrolly
);
734 // ui->song_hscroll->value(target);
736 if(X2
> w()-scrollbuffer
){
738 scrollTo(target
,scrolly
);
739 //ui->song_hscroll->value(target);
744 void Arranger::layout(){
749 fakeh
= tracks
.size()*30;
753 ui
->song_vscroll
->maximum(0);
754 ui
->song_vscroll
->minimum(fakeh
-h());
756 int newsize
= M
-(fakeh
-h());
760 //ui->song_vscroll->slider_size(379);
764 void Arranger::unselect_all(){
766 for(int i
=0; i
<tracks
.size(); i
++){
767 s
= tracks
[i
]->head
->next
;
777 void Arranger::get_outline_color(seqpat
* s
, fltk::Color
* c1
, fltk::Color
* c2
, fltk::Color
* c3
, fltk::Color
* cx
){
780 *c1
= fltk::color(p
->r1
, p
->g1
, p
->b1
);
781 *cx
= fltk::color(p
->rx
, p
->gx
, p
->bx
);
785 seqpat
* over_s
= over_seqpat();
786 if(delete_flag
&& s
->selected
){
787 *c1
= fltk::color(255,0,0);
788 *c2
= fltk::color(255,0,0);
789 *c3
= fltk::color(255,0,0);
799 if(T1
>T2
){SWAP(T1
,T2
);}
800 if(K1
<K2
){SWAP(K1
,K2
);}
801 if(s
->tick
+s
->dur
> T1
&& s
->tick
< T2
&& K
>= K2
&& K
<= K1
){
802 *c1
= fltk::color(0,255,0);
803 *c2
= fltk::color(71,120,59);
804 *c3
= fltk::color(108,229,75);
810 *c1
= fltk::color(255,255,0);
811 *c2
= fltk::color(140,137,46);
812 *c3
= fltk::color(232,255,37);
813 *cx
= fltk::color(128,128,0);
818 *c2
= fltk::color(p
->r2
,p
->g2
,p
->b2
);
819 *c3
= fltk::color(p
->r3
,p
->g3
,p
->b3
);
824 void Arranger::apply_insert(){
826 if(!check_insert_safety()){
831 int T1
= insert_torig
;
832 int T2
= T1
+ insert_toffset
;
833 if(T1
>T2
){SWAP(T1
,T2
);}
835 Command
* c
=new CreateSeqpatBlank(insert_track
,T1
,T2
-T1
);
839 if(T2
>maxt
){relayout();}
842 void Arranger::apply_box(){
849 if(T1
>T2
){SWAP(T1
,T2
);}
850 if(K1
>K2
){SWAP(K1
,K2
);}
852 if(K2
> tracks
.size()-1){K2
= tracks
.size()-1;}
853 for(int i
=K1
; i
<=K2
; i
++){
854 s
= tracks
[i
]->head
->next
;
856 if(s
->tick
+s
->dur
> T1
&& s
->tick
< T2
){
865 void Arranger::apply_delete(){
870 for(int i
=0; i
<tracks
.size(); i
++){
871 s
= tracks
[i
]->head
->next
;
875 tracks
[s
->track
]->modified
= 1;
876 c
=new DeleteSeqpat(s
);
885 unmodify_and_unstick_tracks();
889 void Arranger::apply_move(){
890 if(move_toffset
==0 && move_koffset
==0){
894 if(!check_move_safety()){
902 for(int i
=0; i
<tracks
.size(); i
++){
903 s
= tracks
[i
]->head
->next
;
906 if(s
->selected
&& s
->modified
== 0){
907 int K
= s
->track
+ move_koffset
;
908 int T
= s
->tick
+ move_toffset
;
909 tracks
[s
->track
]->modified
= 1;
910 tracks
[K
]->modified
= 1;
912 c
=new MoveSeqpat(s
,K
,T
);
916 if(T
+s
->dur
> maxt
){relayout();}
924 unmodify_and_unstick_tracks();
927 void Arranger::apply_paste(){
933 c
= new CreateSeqpat(paste_track
,paste_t
,main_sel
,config
.alwayscopy
);
940 void Arranger::apply_rresize(){
941 if(rresize_toffset
==0){
945 if(!check_resize_safety()){
954 for(int i
=0; i
<tracks
.size(); i
++){
955 s
= tracks
[i
]->head
->next
;
958 if(s
->selected
&& s
->modified
== 0){
959 tracks
[i
]->modified
= 1;
962 int T2
= s
->tick
+ s
->dur
+ rresize_toffset
;
965 seqpat
* stmp
= s
->prev
;
966 //c=new ReverseSeqpat(s);
969 c
=new ResizeSeqpat(s
,T2
-T1
);
972 c
=new MoveSeqpat(s
,s
->track
,T1
);
977 if(s
->tick
+s
->dur
> maxt
){relayout();}
983 c
=new ResizeSeqpat(s
,T2
-T1
);
987 if(T2
> maxt
){relayout();}
997 unmodify_and_unstick_tracks();
1000 void Arranger::apply_lresize(){
1001 if(lresize_toffset
==0){
1005 if(!check_resize_safety()){
1014 for(int i
=0; i
<tracks
.size(); i
++){
1015 s
= tracks
[i
]->head
->next
;
1018 if(s
->selected
&& s
->modified
== 0){
1019 tracks
[i
]->modified
= 1;
1021 int T1
= s
->tick
+ lresize_toffset
;
1022 int T2
= s
->tick
+ s
->dur
;
1025 seqpat
* stmp
= s
->prev
;
1026 //c=new ReverseSeqpat(s);
1029 c
=new ResizeSeqpat(s
,T2
-T1
);
1032 c
=new MoveSeqpat(s
,s
->track
,T1
);
1037 if(s
->tick
+s
->dur
> maxt
){relayout();}
1041 T2
= T1
+128; //magic
1043 seqpat
* stmp
= s
->prev
;
1044 c
=new MoveSeqpat(s
,s
->track
,T1
);
1047 c
=new ResizeSeqpat(s
,T2
-T1
);
1052 if(s
->tick
+s
->dur
>maxt
){relayout();}
1062 unmodify_and_unstick_tracks();
1067 int collision_test(int t11
, int t12
, int t21
, int t22
){
1068 return !((t11
< t21
&& t12
<= t21
) ||
1069 (t11
>= t22
&& t12
> t22
)) ? 1 : 0;
1072 int Arranger::check_move_safety(){
1076 for(int i
=0; i
<tracks
.size(); i
++){
1077 s
= tracks
[i
]->head
->next
;
1080 if(i
+move_koffset
< 0 || i
+move_koffset
> tracks
.size()-1 ||
1081 s
->tick
+ move_toffset
< 0){
1084 ptr
= tracks
[i
+move_koffset
]->head
->next
;
1087 ptr
=ptr
->next
; continue;
1089 if(collision_test(s
->tick
+move_toffset
,s
->tick
+s
->dur
+move_toffset
,ptr
->tick
,ptr
->tick
+ptr
->dur
) ){
1104 int Arranger::check_insert_safety(){
1107 int T1
= insert_torig
;
1108 int T2
= T1
+ insert_toffset
;
1111 if(T1
>T2
){SWAP(T1
,T2
);}
1116 if(insert_track
> tracks
.size()-1){
1119 if(tracks
[insert_track
]==NULL
){
1123 s
= tracks
[insert_track
]->head
->next
;
1126 if(collision_test(T1
,T2
,s
->tick
,s
->tick
+s
->dur
)){
1135 int Arranger::check_resize_safety(){
1143 for(int i
=0; i
<tracks
.size(); i
++){
1144 s
= tracks
[i
]->head
->next
;
1147 s
= s
->next
; continue;
1152 T2
= s
->tick
+ s
->dur
+ rresize_toffset
;
1154 else if(lresize_flag
){
1155 T1
= s
->tick
+ lresize_toffset
;
1156 T2
= s
->tick
+ s
->dur
;
1158 if(T1
>T2
){SWAP(T1
,T2
);}
1163 ptr
= tracks
[s
->track
]->head
->next
;
1166 ptr
=ptr
->next
; continue;
1170 S2
= ptr
->tick
+ ptr
->dur
;
1173 S2
+= rresize_toffset
;
1175 else if(lresize_flag
){
1176 S1
+= lresize_toffset
;
1180 if(collision_test(T1
,T2
,S1
,S2
)){
1194 int Arranger::check_paste_safety(){
1199 void Arranger::apply_unclone(){
1202 pattern
* p2
= new pattern(main_sel
->p
);
1203 //does p2 need to have its ref_c set
1204 float a
= randf(0.2,0.8);
1205 while(fabs(p2
->v
- a
)<0.1){
1211 //this creates a copy of the block, but uses a copy of the pattern
1212 seqpat
* s2
= new seqpat(main_sel
,p2
);
1215 c
= new DeleteSeqpat(main_sel
);
1219 c
= new CreateSeqpat(s2
->track
,s2
->tick
,s2
,0);