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;
72 if(fakeh
< h
){fakeh
= h
;}
78 int Arranger::handle(int event
){
94 case fltk::MOUSEWHEEL
:
101 else if(event_dy()<0){
109 if(event_state() && event_key()=='c'){
113 if(event_state() && event_key()=='v'){
117 if(event_state() && event_key()=='z'){
121 if(event_key()==fltk::DeleteKey
){
127 if(zoom_out_key(event_key(),event_state())){
128 //if(event_key()==fltk::LeftKey){
131 zoom
= 30*(1<<zoom_n
)/16;
132 ui
->song_timeline
->zoom
= 30*(1<<zoom_n
)/16;
133 ui
->song_timeline
->update(get_play_position());
134 ui
->song_timeline
->redraw();
140 if(zoom_in_key(event_key(),event_state())){
141 //if(event_key()==fltk::RightKey){
144 zoom
= 30*(1<<zoom_n
)/16;
145 ui
->song_timeline
->zoom
= 30*(1<<zoom_n
)/16;
146 ui
->song_timeline
->update(get_play_position());
147 ui
->song_timeline
->redraw();
156 if(event_button()==1){//left mouse
157 seqpat
* s
= over_seqpat();
159 if(color_flag
){//do nothing
161 else if(event_state()&fltk::SHIFT
){//begin box
167 box_t1
=xpix2tick(X
+scrollx
);
169 box_k1
=(Y
+scrolly
)/30;
174 insert_torig
= xpix2tick(X
+scrollx
)/q_tick
*q_tick
;
175 insert_toffset
= q_tick
;
176 insert_track
= (Y
+scrolly
) / 30;
185 color_orig_h
= color_sel
->h
;
186 color_orig_v
= color_sel
->v
;
187 color_h
= color_orig_h
;
188 color_v
= color_orig_v
;
191 if(!s
->selected
&& !(event_state()&SHIFT
)){
195 if(fltk::event_clicks() > 0){//'double click'
196 ui
->piano_roll
->load(s
);
197 ui
->event_edit
->load(s
);
198 ui
->piano_roll
->scrollTo(s
->scrollx
,s
->scrolly
);
199 ui
->pattern_timeline
->update(get_play_position());
200 ui
->keyboard
->cur_port
= tracks
[s
->track
]->port
;
201 ui
->keyboard
->cur_chan
= tracks
[s
->track
]->chan
;
202 ui
->track_info
->set_rec(s
->track
);
203 set_rec_track(s
->track
);
208 if(over_lhandle(s
)){//begin resize
210 lresize_torig
= s
->tick
;
213 else if(over_rhandle(s
)){//begin resizemove
215 rresize_torig
= s
->tick
+s
->dur
;
221 move_torig
= s
->tick
;
223 move_korig
= s
->track
;
227 move_offset
= xpix2tick(X
+scrollx
)/q_tick
*q_tick
- s
->tick
;
231 else if(event_button()==2){//middle mouse
232 seqpat
* s
= over_seqpat();
236 s
->p
->regen_colors();
242 paste_t
= quantize(xpix2tick(X
+scrollx
));
243 paste_track
= (Y
+scrolly
) / 30;
246 else if(event_button()==3){//right mouse
247 seqpat
* s
= over_seqpat();
249 seqpat
* ptr
= tracks
[s
->track
]->head
->next
;
253 ptr
->p
->regen_colors();
267 delete_sel
= s
;//this line needs to be removed
280 box_t2
= xpix2tick(X
+scrollx
);
281 box_k2
= (Y
+scrolly
)/30;
283 if(color_flag
&& color_sel
){
284 color_sel
->h
= color_orig_h
+ (color_orig_x
- X
)/1.0;
285 color_sel
->v
= color_orig_v
+ (color_orig_y
- Y
)/100.0;
286 color_sel
->regen_colors();
287 color_h
= color_sel
->h
;
288 color_v
= color_sel
->v
;
289 set_default_hsv_value(color_v
);
292 insert_toffset
= xpix2tick(X
+scrollx
)/q_tick
*q_tick
+q_tick
-insert_torig
;
293 if(insert_toffset
<=0){
294 insert_toffset
-= q_tick
;
296 insert_track
= (Y
+scrolly
) / 30;
298 else if(rresize_flag
){
299 rresize_toffset
= xpix2tick(X
+scrollx
)/128*128 - rresize_torig
;
301 else if(lresize_flag
){
302 lresize_toffset
= xpix2tick(X
+scrollx
)/128*128 - lresize_torig
;
305 move_toffset
= quantize(xpix2tick(X
+scrollx
)) - move_torig
- move_offset
;
306 move_koffset
= (Y
+scrolly
) / 30 - move_korig
;
309 paste_t
= quantize(xpix2tick(X
+scrollx
));
310 paste_track
= (Y
+scrolly
) / 30;
315 if(event_button()==1){
328 else if(rresize_flag
){
332 last_handle
->lhandle
= 0;
333 last_handle
->rhandle
= 0;
336 else if(lresize_flag
){
340 last_handle
->lhandle
= 0;
341 last_handle
->rhandle
= 0;
348 else if(event_button()==2){
354 else if(event_button()==3){
355 seqpat
* over_s
= over_seqpat();
356 if(delete_flag
&& over_s
){
357 if(over_s
->selected
){
370 if(color_flag
){break;}
371 seqpat
* s
= over_seqpat();
373 if(over_rhandle(s
)){s
->rhandle
= 1;}
374 else{s
->rhandle
= 0;}
375 if(over_lhandle(s
)){s
->lhandle
= 1;}
376 else{s
->lhandle
= 0;}
377 if(s
!= last_handle
){
379 last_handle
->rhandle
= 0;
380 last_handle
->lhandle
= 0;
386 else if(last_handle
){
387 last_handle
->rhandle
= 0;
388 last_handle
->lhandle
= 0;
398 void Arranger::draw(){
400 fltk::push_clip(0,0,w(),h());
402 fltk::setfont(fltk::HELVETICA
,8);
404 fltk::setcolor(fltk::GRAY05
);
405 fltk::fillrect(0,0,w(),h());
407 fltk::setcolor(fltk::GRAY20
);
408 int M
= config
.beats_per_measure
;
410 for(int i
=1; I
<w(); i
++){
411 I
= i
*zoom
*M
/4 - scrollx
;
413 fltk::fillrect(I
,0,1,h());
416 fltk::setcolor(fltk::GRAY50
);
417 int P
= config
.measures_per_phrase
;
420 for(int i
=1; I
<w(); i
++){
421 I
= i
*zoom
*4*P
*M
/4/4 - scrollx
;
423 fltk::fillrect(I
,0,1,h());
430 fltk::setcolor(fltk::BLUE
);
431 int T1
= insert_torig
;
432 int T2
= T1
+ insert_toffset
;
434 if(T1
>T2
){SWAP(T1
,T2
);}
435 int X
= tick2xpix(T1
)+1 - scrollx
;
436 int Y
= insert_track
*30 - scrolly
;
437 int W
= tick2xpix(T2
)-tick2xpix(T1
) - 1;
438 fltk::fillrect(X
,Y
,W
,28);
442 if(check_move_safety()){
443 fltk::setcolor(fltk::MAGENTA
);
446 fltk::setcolor(fltk::RED
);
449 for(int i
=0; i
<tracks
.size(); i
++){
450 seqpat
* s
= tracks
[i
]->head
->next
;
453 int X
= tick2xpix(s
->tick
+ move_toffset
) - scrollx
;
454 int Y
= (s
->track
+ move_koffset
)*30 - scrolly
;
455 int W
= tick2xpix(s
->dur
);
456 fltk::fillrect(X
+1,Y
+1,W
-1,1);
457 fltk::fillrect(X
+1,Y
+1,1,29-1);
458 fltk::fillrect(X
+1,Y
+29-1,W
-1,1);
459 fltk::fillrect(X
+W
-1,Y
+1,1,29-1);
467 fltk::setcolor(fltk::GREEN
);
468 int X
= tick2xpix(paste_t
)+1 - scrollx
;
469 int Y
= paste_track
*30 - scrolly
;
470 int W
= tick2xpix(main_sel
->dur
);
471 fltk::fillrect(X
,Y
,W
-1,1);
472 fltk::fillrect(X
,Y
+28,W
-1,1);
473 fltk::fillrect(X
,Y
,1,28);
474 fltk::fillrect(X
+W
-2,Y
,1,28);
479 fltk::setcolor(fltk::GREEN
);
485 if(X1
>X2
){SWAP(X1
,X2
);}
486 if(Y1
>Y2
){SWAP(Y1
,Y2
);}
487 fltk::fillrect(X1
,Y1
,X2
-X1
,1);
488 fltk::fillrect(X1
,Y1
,1,Y2
-Y1
);
489 fltk::fillrect(X2
,Y1
,1,Y2
-Y1
);
490 fltk::fillrect(X1
,Y2
,X2
-X1
,1);
497 fltk::Color c1
,c2
,c3
,cx
;
500 for(int i
=0; i
<tracks
.size(); i
++){
502 s
= tracks
[i
]->head
->next
;
507 get_outline_color(s
,&c1
,&c2
,&c3
,&cx
);
511 int R1
= lresize_flag
&&s
->selected
? lresize_toffset
: 0;
512 int R2
= rresize_flag
&&s
->selected
? rresize_toffset
: 0;
515 int T2
= s
->tick
+s
->dur
+R2
;
517 if(T1
> T2
){SWAP(T1
,T2
)};
519 int X
= tick2xpix(T1
)+1 - scrollx
;
520 int Y
= s
->track
* 30 - scrolly
;
521 int W
= tick2xpix(T2
)-tick2xpix(T1
)-1;
523 if(rresize_flag
&& s
->selected
&& T1
==T2
){
524 W
= tick2xpix(128)-1;
526 if(lresize_flag
&& s
->selected
&& T1
==T2
){
527 W
= tick2xpix(128)-1;
530 fillrect(X
+1,Y
+1,W
-2,27);
535 fillrect(X
+W
-1,Y
,1,29);
536 fillrect(X
,Y
+28,W
-1,1);
542 fltk::push_clip(tick2xpix(T1
)-scrollx
,s
->track
*30-scrolly
,tick2xpix(T2
-T1
),30);
544 if(s
->rhandle
&& !rresize_flag
){
547 setcolor(fltk::color(128,0,0));
551 X
= tick2xpix(s
->tick
+s
->dur
) - W
- 1 - scrollx
;
552 Y
= s
->track
*30 - scrolly
;
553 addvertex(X
+W
,Y
+28/2);
559 if(s
->lhandle
&& !lresize_flag
){
562 setcolor(fltk::color(128,0,0));
565 X
= tick2xpix(s
->tick
)+1-scrollx
;
566 Y
= s
->track
*30 - scrolly
;
576 mevent
* e
= s
->p
->events
;
578 if(e
->tick
>= s
->dur
){
581 if(e
->type
== MIDI_NOTE_ON
){
582 X
= tick2xpix(e
->tick
) + tick2xpix(s
->tick
)+2 - scrollx
;
583 Y
= s
->track
*30 + 27 - e
->value1
*27/127;
584 W
= tick2xpix(e
->dur
);
592 int total
= s
->layer_total();
594 fltk::setcolor(fltk::BLACK
);
595 int X
= tick2xpix(s
->tick
) - scrollx
;
596 int Y
= s
->track
* 30 - scrolly
;
597 int count
= s
->layer_index()+1;
599 snprintf(buf
,16,"%d / %d",count
,total
);
600 fltk::drawtext(buf
,X
+2,Y
+27);
614 void Arranger::scrollTo(int X
, int Y
){
616 if(is_backend_playing() && config
.follow
){
617 int pos
= tick2xpix(get_play_position());
618 if(pos
< X
|| pos
> X
+ w() - scrollbuffer
- 30){
619 ui
->song_hscroll
->value(scrollx
);
627 ui
->song_hscroll
->value(scrollx
);
631 ui
->song_timeline
->scroll
= X
;
632 ui
->song_timeline
->redraw();
633 ui
->track_info
->scroll
= Y
;
634 ui
->track_info
->redraw();
639 seqpat
* Arranger::over_seqpat(){
640 int track
= (event_y()+scrolly
) / 30;
641 if(track
>= tracks
.size()){
644 int tick
= xpix2tick(event_x()+scrollx
);
645 seqpat
* s
= tfind
<seqpat
>(tracks
[track
]->head
,tick
);
647 if(tick
< s
->tick
+s
->dur
){
655 //true if over right handle of s
656 int Arranger::over_rhandle(seqpat
* s
){
659 int X1
= tick2xpix(s
->tick
) - scrollx
;
660 int X2
= X1
+ tick2xpix(s
->dur
);
661 int Y1
= s
->track
* 30 + 1 - scrolly
;
664 if(tick2xpix(s
->dur
) < 10){
668 return (Y
> Y1
&& Y
< Y2
&& X
< X2
&& X
> X2
- 5);
671 //true if over left handle of s
672 int Arranger::over_lhandle(seqpat
* s
){
675 int X1
= tick2xpix(s
->tick
) - scrollx
;
676 int X2
= X1
+ tick2xpix(s
->dur
);
677 int Y1
= s
->track
* 30 + 1 - scrolly
;
680 if(tick2xpix(s
->dur
) < 10){
684 if(Y
> Y1
&& Y
< Y2
&& X
< X1
+ 5 + 1 && X
> X1
+1){
685 //printf("success\n");
687 return (Y
> Y1
&& Y
< Y2
&& X
< X1
+ 5 + 1 && X
> X1
+1);
690 // 4=beats per measure, 128=ticks per beat, 30=width of measure in pixels
691 int Arranger::tick2xpix(int tick
){
692 return tick
*zoom
/(128*4);
695 int Arranger::xpix2tick(int xpix
){
696 return xpix
* (128*4) /zoom
;
699 int Arranger::quantize(int tick
){
700 return tick
/q_tick
* q_tick
;
704 void Arranger::update(int pos
){
705 if(!is_backend_playing()){
708 int X1
= tick2xpix(pos
);
709 int X2
= X1
- scrollx
;
711 int target
= X1
-50<0?0:X1
-50;
712 scrollTo(target
,scrolly
);
713 // ui->song_hscroll->value(target);
715 if(X2
> w()-scrollbuffer
){
717 scrollTo(target
,scrolly
);
718 //ui->song_hscroll->value(target);
723 void Arranger::layout(){
728 fakeh
= tracks
.size()*30;
732 ui
->song_vscroll
->maximum(0);
733 ui
->song_vscroll
->minimum(fakeh
-h());
734 int M
= ui
->song_vscroll
->h() - 30;
735 int newsize
= M
-(fakeh
-h());
739 ui
->song_vscroll
->slider_size(newsize
);
743 void Arranger::unselect_all(){
745 for(int i
=0; i
<tracks
.size(); i
++){
746 s
= tracks
[i
]->head
->next
;
756 void Arranger::get_outline_color(seqpat
* s
, fltk::Color
* c1
, fltk::Color
* c2
, fltk::Color
* c3
, fltk::Color
* cx
){
759 *c1
= fltk::color(p
->r1
, p
->g1
, p
->b1
);
760 *cx
= fltk::color(p
->rx
, p
->gx
, p
->bx
);
764 seqpat
* over_s
= over_seqpat();
765 if(delete_flag
&& s
->selected
){
766 *c1
= fltk::color(255,0,0);
767 *c2
= fltk::color(255,0,0);
768 *c3
= fltk::color(255,0,0);
778 if(T1
>T2
){SWAP(T1
,T2
);}
779 if(K1
<K2
){SWAP(K1
,K2
);}
780 if(s
->tick
+s
->dur
> T1
&& s
->tick
< T2
&& K
>= K2
&& K
<= K1
){
781 *c1
= fltk::color(0,255,0);
782 *c2
= fltk::color(71,120,59);
783 *c3
= fltk::color(108,229,75);
789 *c1
= fltk::color(255,255,0);
790 *c2
= fltk::color(140,137,46);
791 *c3
= fltk::color(232,255,37);
792 *cx
= fltk::color(128,128,0);
797 *c2
= fltk::color(p
->r2
,p
->g2
,p
->b2
);
798 *c3
= fltk::color(p
->r3
,p
->g3
,p
->b3
);
803 void Arranger::apply_insert(){
805 if(!check_insert_safety()){
810 int T1
= insert_torig
;
811 int T2
= T1
+ insert_toffset
;
812 if(T1
>T2
){SWAP(T1
,T2
);}
814 Command
* c
=new CreateSeqpatBlank(insert_track
,T1
,T2
-T1
);
818 if(T2
>maxt
){relayout();}
821 void Arranger::apply_box(){
828 if(T1
>T2
){SWAP(T1
,T2
);}
829 if(K1
>K2
){SWAP(K1
,K2
);}
831 if(K2
> tracks
.size()-1){K2
= tracks
.size()-1;}
832 for(int i
=K1
; i
<=K2
; i
++){
833 s
= tracks
[i
]->head
->next
;
835 if(s
->tick
+s
->dur
> T1
&& s
->tick
< T2
){
844 void Arranger::apply_delete(){
849 for(int i
=0; i
<tracks
.size(); i
++){
850 s
= tracks
[i
]->head
->next
;
854 tracks
[s
->track
]->modified
= 1;
855 c
=new DeleteSeqpat(s
);
864 unmodify_and_unstick_tracks();
868 void Arranger::apply_move(){
869 if(move_toffset
==0 && move_koffset
==0){
873 if(!check_move_safety()){
881 for(int i
=0; i
<tracks
.size(); i
++){
882 s
= tracks
[i
]->head
->next
;
885 if(s
->selected
&& s
->modified
== 0){
886 int K
= s
->track
+ move_koffset
;
887 int T
= s
->tick
+ move_toffset
;
888 tracks
[s
->track
]->modified
= 1;
889 tracks
[K
]->modified
= 1;
891 c
=new MoveSeqpat(s
,K
,T
);
895 if(T
+s
->dur
> maxt
){relayout();}
903 unmodify_and_unstick_tracks();
906 void Arranger::apply_paste(){
912 c
= new CreateSeqpat(paste_track
,paste_t
,main_sel
,0);
919 void Arranger::apply_rresize(){
920 if(rresize_toffset
==0){
924 if(!check_resize_safety()){
933 for(int i
=0; i
<tracks
.size(); i
++){
934 s
= tracks
[i
]->head
->next
;
937 if(s
->selected
&& s
->modified
== 0){
938 tracks
[i
]->modified
= 1;
941 int T2
= s
->tick
+ s
->dur
+ rresize_toffset
;
944 seqpat
* stmp
= s
->prev
;
945 //c=new ReverseSeqpat(s);
948 c
=new ResizeSeqpat(s
,T2
-T1
);
951 c
=new MoveSeqpat(s
,s
->track
,T1
);
956 if(s
->tick
+s
->dur
> maxt
){relayout();}
962 c
=new ResizeSeqpat(s
,T2
-T1
);
966 if(T2
> maxt
){relayout();}
976 unmodify_and_unstick_tracks();
979 void Arranger::apply_lresize(){
980 if(lresize_toffset
==0){
984 if(!check_resize_safety()){
993 for(int i
=0; i
<tracks
.size(); i
++){
994 s
= tracks
[i
]->head
->next
;
997 if(s
->selected
&& s
->modified
== 0){
998 tracks
[i
]->modified
= 1;
1000 int T1
= s
->tick
+ lresize_toffset
;
1001 int T2
= s
->tick
+ s
->dur
;
1004 seqpat
* stmp
= s
->prev
;
1005 //c=new ReverseSeqpat(s);
1008 c
=new ResizeSeqpat(s
,T2
-T1
);
1011 c
=new MoveSeqpat(s
,s
->track
,T1
);
1016 if(s
->tick
+s
->dur
> maxt
){relayout();}
1020 T2
= T1
+128; //magic
1022 seqpat
* stmp
= s
->prev
;
1023 c
=new MoveSeqpat(s
,s
->track
,T1
);
1026 c
=new ResizeSeqpat(s
,T2
-T1
);
1031 if(s
->tick
+s
->dur
>maxt
){relayout();}
1041 unmodify_and_unstick_tracks();
1046 int collision_test(int t11
, int t12
, int t21
, int t22
){
1047 return !((t11
< t21
&& t12
<= t21
) ||
1048 (t11
>= t22
&& t12
> t22
)) ? 1 : 0;
1051 int Arranger::check_move_safety(){
1055 for(int i
=0; i
<tracks
.size(); i
++){
1056 s
= tracks
[i
]->head
->next
;
1059 if(i
+move_koffset
< 0 || i
+move_koffset
> tracks
.size()-1 ||
1060 s
->tick
+ move_toffset
< 0){
1063 ptr
= tracks
[i
+move_koffset
]->head
->next
;
1066 ptr
=ptr
->next
; continue;
1068 if(collision_test(s
->tick
+move_toffset
,s
->tick
+s
->dur
+move_toffset
,ptr
->tick
,ptr
->tick
+ptr
->dur
) ){
1083 int Arranger::check_insert_safety(){
1086 int T1
= insert_torig
;
1087 int T2
= T1
+ insert_toffset
;
1090 if(T1
>T2
){SWAP(T1
,T2
);}
1095 if(insert_track
> tracks
.size()-1){
1098 if(tracks
[insert_track
]==NULL
){
1102 s
= tracks
[insert_track
]->head
->next
;
1105 if(collision_test(T1
,T2
,s
->tick
,s
->tick
+s
->dur
)){
1114 int Arranger::check_resize_safety(){
1122 for(int i
=0; i
<tracks
.size(); i
++){
1123 s
= tracks
[i
]->head
->next
;
1126 s
= s
->next
; continue;
1131 T2
= s
->tick
+ s
->dur
+ rresize_toffset
;
1133 else if(lresize_flag
){
1134 T1
= s
->tick
+ lresize_toffset
;
1135 T2
= s
->tick
+ s
->dur
;
1137 if(T1
>T2
){SWAP(T1
,T2
);}
1142 ptr
= tracks
[s
->track
]->head
->next
;
1145 ptr
=ptr
->next
; continue;
1149 S2
= ptr
->tick
+ ptr
->dur
;
1152 S2
+= rresize_toffset
;
1154 else if(lresize_flag
){
1155 S1
+= lresize_toffset
;
1159 if(collision_test(T1
,T2
,S1
,S2
)){
1173 int Arranger::check_paste_safety(){