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 PianoRoll::PianoRoll(int x
, int y
, int w
, int h
, const char* label
= 0) : fltk::Widget(x
, y
, w
, h
, label
) {
60 int PianoRoll::handle(int event
){
68 if(event_state(CTRL
) && event_key()=='c'){
69 //printf("roll copy\n");
72 if(zoom_out_key(event_key(),event_state())){
75 set_zoom(30*(1<<zoom_n
)/16);
76 ui
->pattern_timeline
->zoom
= zoom
;
77 ui
->pattern_timeline
->update(get_play_position());
78 ui
->pattern_timeline
->redraw();
79 ui
->event_edit
->zoom
= zoom
;
80 ui
->event_edit
->redraw();
85 if(zoom_in_key(event_key(),event_state())){
88 set_zoom(30*(1<<zoom_n
)/16);
89 ui
->pattern_timeline
->zoom
= zoom
;
90 ui
->pattern_timeline
->update(get_play_position());
91 ui
->pattern_timeline
->redraw();
92 ui
->event_edit
->zoom
= zoom
;
93 ui
->event_edit
->redraw();
101 if(event_button()==1){//left mouse
102 if(over_note()==NULL
){//new note init
104 new_left_t
= quantize(xpix2tick(event_x()));
105 new_orig_t
= new_left_t
;
106 new_note
= ypix2note(event_y(),1);
107 new_right_t
= new_left_t
+ q_tick
;
109 last_note
= new_note
;
110 if(config
.playinsert
){
111 ui
->keyboard
->play_note(last_note
,0);
115 //if shift, add to selection
116 main_sel
= over_note();
117 if(over_handle(main_sel
)){//begin resize or resize move
121 move_t
= quantize(main_sel
->tick
);
122 move_offset
= quantize(xpix2tick(event_x())) - move_t
;
123 //move_track = event_y() / 30;
124 move_note
= ypix2note(event_y(),1);
126 last_note
= move_note
;
128 ui
->keyboard
->play_note(last_note
,0);
134 else if(event_button()==2){//middle mouse
135 //button initiates paste
137 else if(event_button()==3){//right mouse
138 if(over_note()==NULL
){//begin box
141 else{//set up for deletion
143 main_sel
= over_note();
150 new_right_t
= quantize(xpix2tick(event_x())+q_tick
);
151 if(new_right_t
<= new_orig_t
){
152 new_left_t
= new_right_t
- q_tick
;
153 new_right_t
= new_orig_t
;
156 new_left_t
= new_orig_t
;
158 new_note
= ypix2note(event_y(),1);
159 if(new_note
!= last_note
){
160 if(config
.playinsert
){//play on insert
161 ui
->keyboard
->release_note(last_note
,0);
162 ui
->keyboard
->play_note(new_note
,0);
164 last_note
= new_note
;
170 move_t
= quantize(xpix2tick(event_x())) - move_offset
;
171 move_note
= ypix2note(event_y(),1);
172 if(move_note
!= last_note
){
173 if(config
.playmove
){//play on move
174 ui
->keyboard
->release_note(last_note
,0);
175 ui
->keyboard
->play_note(move_note
,0);
177 last_note
= move_note
;
183 if(event_button()==1){
184 if(new_drag
&& new_note
< 128 && new_note
>= 0){
186 c
=new CreateNote(p
,new_note
,127,new_left_t
,new_right_t
-new_left_t
);
189 ui
->keyboard
->release_note(new_note
,0);
190 ui
->keyboard
->redraw();
191 ui
->event_edit
->has
[0]=1;
192 ui
->event_edit
->has
[1]=1;
193 ui
->event_edit
->redraw();
194 ui
->event_menu
->redraw();
196 else if(move_flag
&& move_note
< 128 && move_note
>= 0){
197 int play_pos
= get_play_position();
198 mevent
* e
= main_sel
;
199 track
* tr
= tracks
[cur_seqpat
->track
];
200 if(play_pos
> e
->tick
&& play_pos
< e
->tick
+ e
->dur
){
201 midi_note_off(e
->value1
,tr
->chan
,tr
->port
);
203 int old_note
= main_sel
->value1
;
204 c
=new MoveNote(cur_seqpat
->p
,main_sel
,move_t
,move_note
);
208 int cur_chan
= tracks
[cur_seqpat
->track
]->chan
;
209 int cur_port
= tracks
[cur_seqpat
->track
]->port
;
210 midi_note_off(old_note
,cur_chan
,cur_port
);
212 ui
->keyboard
->release_note(move_note
,0);
213 ui
->keyboard
->redraw();
214 ui
->event_edit
->redraw();
219 if(event_button()==3){
220 if(delete_flag
&& over_note() == main_sel
){
221 //here we need more branches for deleting the entire selection
222 c
=new DeleteNote(cur_seqpat
->p
, main_sel
);
226 ui
->event_edit
->redraw();
237 void PianoRoll::draw(){
238 fltk::setcolor(fltk::GRAY05
);
239 fltk::fillrect(0,0,w(),h());
241 fltk::setcolor(fltk::GRAY20
);
242 for(int i
=12; i
<h(); i
+=12){
243 fltk::drawline(0,i
,w(),i
);
245 for(int i
=zoom
; i
<w(); i
+=zoom
){
246 fltk::drawline(i
,0,i
,h());
249 fltk::setcolor(fltk::GRAY30
);
250 for(int i
=12*5; i
<h(); i
+=12*7){
251 fltk::drawline(0,i
,w(),i
);
254 fltk::setcolor(fltk::GRAY50
);
255 for(int i
=zoom
*4; i
<w(); i
+=zoom
*4){
256 fltk::drawline(i
,0,i
,h());
259 fltk::setcolor(fltk::WHITE
);
260 int M
= config
.beats_per_measure
;
261 for(int i
=zoom
*4*M
; i
<w(); i
+=zoom
*4*M
){
262 fltk::fillrect(i
,0,1,h());
265 fltk::setcolor(fltk::color(128,0,0));
266 int rightend
= tick2xpix(cur_seqpat
->dur
);
267 fltk::fillrect(rightend
,0,1,h());
269 fltk::setcolor(fltk::color(128,128,0));
270 fltk::drawline(0,12*40,w(),12*40);
273 fltk::setcolor(fltk::BLUE
);
274 int X
= tick2xpix(new_left_t
)+1;
275 int Y
= note2ypix(new_note
);
276 int W
= tick2xpix(new_right_t
) - X
;
277 fltk::fillrect(X
,Y
,W
,11);
281 fltk::setcolor(fltk::color(255,0,0));
282 int X
= tick2xpix(move_t
)+1;
283 int Y
= note2ypix(move_note
);
284 int W
= tick2xpix(main_sel
->dur
);
285 fltk::fillrect(X
,Y
,W
-1,1);
286 fltk::fillrect(X
,Y
+11,W
-1,1);
287 fltk::fillrect(X
,Y
,1,11);
288 fltk::fillrect(X
+W
-2,Y
,1,11);
292 mevent
* e
= cur_seqpat
->p
->events
->next
;
297 if(e
->type
== MIDI_NOTE_ON
){
298 //fltk::fillrect(tick2xpix(e->tick),note2ypix(e->value),e->dur,11);
299 int X
= tick2xpix(e
->tick
) + 1;
300 int Y
= note2ypix(e
->value1
);
301 int W
= tick2xpix(e
->tick
+e
->dur
) - X
;
312 fltk::setcolor(fltk::color(c1
,c2
,c3
));
313 fltk::fillrect(X
+1,Y
+1,W
-1,10);
315 fltk::setcolor(fltk::color(c1
*3/4,c2
*3/4,c3
*3/4));
316 fltk::fillrect(X
,Y
+11,W
,1);
317 fltk::fillrect(X
+W
-1,Y
+1,1,11);
319 fltk::setcolor(fltk::color(c1
*3/4+64,c2
*3/4+64,c3
*3/4+64));
320 fltk::fillrect(X
,Y
,W
,1);
321 fltk::fillrect(X
,Y
,1,11);
329 static int kludge
= 4; //very powerful magic
330 void PianoRoll::layout(){
332 /* the kludge is used so the fltk::ScrollGroup can update
333 widgets not contained within it. Better solution, the
334 scrollgroup could do its callback if it scrolls.
335 Subclassing fltk::ScrollGroup to add this behavior failed. */
341 ui
->pattern_timeline
->zoom
= zoom
;
342 ui
->event_edit
->zoom
= zoom
;
345 int W
= tick2xpix(cur_seqpat
->dur
);
349 int wp
= ui
->pattern_scroll
->w();
354 int hp
= ui
->pattern_scroll
->h();
359 int xp
= ui
->pattern_scroll
->xposition();
360 int yp
= ui
->pattern_scroll
->yposition();
364 ui
->pattern_scroll
->scrollTo(xp
,yp
);
367 ui
->pattern_timeline
->scroll
= xp
;
368 ui
->event_edit
->scroll
= xp
;
369 ui
->keyboard
->scroll
= yp
;
372 cur_seqpat
->scrolly
= yp
;
373 cur_seqpat
->scrollx
= xp
;
377 ui
->pattern_timeline
->redraw();
378 ui
->event_edit
->redraw();
381 ui
->keyboard
->redraw();
390 void PianoRoll::load(seqpat
* s
){
392 cur_track
= tracks
[s
->track
];
393 int W
= tick2xpix(s
->dur
);
396 ui
->pattern_timeline
->ticks_offset
= s
->tick
;
399 int PianoRoll::note2ypix(int note
){
400 int udy
= 6*(note
+ (note
+7)/12 + note
/12) + 12;
401 return h() - udy
+ 1;
404 int PianoRoll::tick2xpix(int tick
){
405 return tick
*zoom
*4 / 128;
408 int PianoRoll::xpix2tick(int xpix
){
409 return xpix
*128 / (zoom
*4);
412 int PianoRoll::quantize(int tick
){
413 return tick
/q_tick
* q_tick
;
417 void PianoRoll::set_zoom(int z
){
420 //int W = tick2xpix(cur_seqpat->dur);
425 mevent
* PianoRoll::over_note(){
426 mevent
* e
= cur_seqpat
->p
->events
->next
;
430 if(e
->type
== MIDI_NOTE_ON
){
431 cy
= note2ypix(e
->value1
);
432 lx
= tick2xpix(e
->tick
);
433 rx
= tick2xpix(e
->tick
+e
->dur
);
434 if(event_x() > lx
&& event_x() < rx
&&
435 event_y() < cy
+12 && event_y() > cy
){
445 int PianoRoll::over_handle(mevent
* e
){
451 void PianoRoll::update(int pos
){
452 if(!is_backend_playing()){
455 int wp
= ui
->pattern_scroll
->w();
456 int xp
= ui
->pattern_scroll
->xposition();
457 int yp
= ui
->pattern_scroll
->yposition();
458 int X1
= tick2xpix(pos
-cur_seqpat
->tick
);
464 ui
->pattern_scroll
->scrollTo(X1
-50<0?0:X1
-50,yp
);
467 ui
->pattern_scroll
->scrollTo(X1
-50,yp
);