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>
37 extern std::vector
<track
*> tracks
;
41 PianoRoll::PianoRoll(int x
, int y
, int w
, int h
, const char* label
= 0) : fltk::Widget(x
, y
, w
, h
, label
) {
53 int PianoRoll::handle(int event
){
61 if(event_state(CTRL
) && event_key()=='c'){
62 //printf("roll copy\n");
65 if(event_key() == '-'){
68 set_zoom(30*(1<<zoom_n
)/16);
69 ui
->pattern_timeline
->zoom
= zoom
;
70 ui
->pattern_timeline
->redraw();
71 ui
->event_edit
->zoom
= zoom
;
72 ui
->event_edit
->redraw();
77 if(event_key() == '='){
80 set_zoom(30*(1<<zoom_n
)/16);
81 ui
->pattern_timeline
->zoom
= zoom
;
82 ui
->pattern_timeline
->redraw();
83 ui
->event_edit
->zoom
= zoom
;
84 ui
->event_edit
->redraw();
92 if(event_button()==1){//left mouse
93 if(over_note()==NULL
){//new note init
95 new_left_t
= quantize(xpix2tick(event_x()));
96 new_orig_t
= new_left_t
;
97 new_note
= ypix2note(event_y(),1);
98 new_right_t
= new_left_t
+ q_tick
;
100 last_note
= new_note
;
101 if(1){//play on insert
102 ui
->keyboard
->play_note(last_note
,0);
106 //if shift, add to selection
107 main_sel
= over_note();
108 if(over_handle(main_sel
)){//begin resize or resize move
112 move_t
= main_sel
->tick
;
113 move_offset
= quantize(xpix2tick(event_x())) - move_t
;
114 //move_track = event_y() / 30;
115 move_note
= ypix2note(event_y(),1);
117 last_note
= move_note
;
119 ui
->keyboard
->play_note(last_note
,0);
125 else if(event_button()==2){//middle mouse
126 //button initiates paste
128 else if(event_button()==3){//right mouse
129 if(over_note()==NULL
){//begin box
132 else{//set up for deletion
134 main_sel
= over_note();
141 new_right_t
= quantize(xpix2tick(event_x())+q_tick
);
142 if(new_right_t
<= new_orig_t
){
143 new_left_t
= new_right_t
- q_tick
;
144 new_right_t
= new_orig_t
;
147 new_left_t
= new_orig_t
;
149 new_note
= ypix2note(event_y(),1);
150 if(new_note
!= last_note
){
151 if(1){//play on insert
152 ui
->keyboard
->release_note(last_note
,0);
153 ui
->keyboard
->play_note(new_note
,0);
155 last_note
= new_note
;
161 move_t
= quantize(xpix2tick(event_x())) - move_offset
;
162 move_note
= ypix2note(event_y(),1);
163 if(move_note
!= last_note
){
165 ui
->keyboard
->release_note(last_note
,0);
166 ui
->keyboard
->play_note(move_note
,0);
168 last_note
= move_note
;
174 if(event_button()==1){
175 if(new_drag
&& new_note
< 128 && new_note
>= 0){
177 c
=new CreateNote(p
,new_note
,127,new_left_t
,new_right_t
-new_left_t
);
180 ui
->keyboard
->release_note(new_note
,0);
181 ui
->keyboard
->redraw();
183 else if(move_flag
&& move_note
< 128 && move_note
>= 0){
184 int play_pos
= get_play_position();
185 mevent
* e
= main_sel
;
186 track
* tr
= tracks
[cur_seqpat
->track
];
187 if(play_pos
> e
->tick
&& play_pos
< e
->tick
+ e
->dur
){
188 midi_note_off(e
->value1
,tr
->chan
,tr
->port
);
190 int old_note
= main_sel
->value1
;
191 c
=new MoveNote(cur_seqpat
->p
,main_sel
,move_t
,move_note
);
195 int cur_chan
= tracks
[cur_seqpat
->track
]->chan
;
196 int cur_port
= tracks
[cur_seqpat
->track
]->port
;
197 midi_note_off(old_note
,cur_chan
,cur_port
);
199 ui
->keyboard
->release_note(move_note
,0);
200 ui
->keyboard
->redraw();
205 if(event_button()==3){
206 if(delete_flag
&& over_note() == main_sel
){
207 //here we need more branches for deleting the entire selection
208 c
=new DeleteNote(cur_seqpat
->p
, main_sel
);
220 void PianoRoll::draw(){
221 fltk::setcolor(fltk::GRAY05
);
222 fltk::fillrect(0,0,w(),h());
224 fltk::setcolor(fltk::GRAY20
);
225 for(int i
=12; i
<h(); i
+=12){
226 fltk::drawline(0,i
,w(),i
);
228 for(int i
=zoom
; i
<w(); i
+=zoom
){
229 fltk::drawline(i
,0,i
,h());
232 fltk::setcolor(fltk::GRAY30
);
233 for(int i
=12*5; i
<h(); i
+=12*7){
234 fltk::drawline(0,i
,w(),i
);
237 fltk::setcolor(fltk::GRAY50
);
238 for(int i
=zoom
*4; i
<w(); i
+=zoom
*4){
239 fltk::drawline(i
,0,i
,h());
242 fltk::setcolor(fltk::color(128,128,0));
243 fltk::drawline(0,12*40,w(),12*40);
246 fltk::setcolor(fltk::BLUE
);
247 int X
= tick2xpix(new_left_t
)+1;
248 int Y
= note2ypix(new_note
);
249 int W
= tick2xpix(new_right_t
) - X
;
250 fltk::fillrect(X
,Y
,W
,11);
254 fltk::setcolor(fltk::RED
);
255 int X
= tick2xpix(move_t
)+1;
256 int Y
= note2ypix(move_note
);
257 int W
= tick2xpix(main_sel
->dur
);
258 fltk::fillrect(X
,Y
,W
-1,1);
259 fltk::fillrect(X
,Y
+11,W
-1,1);
260 fltk::fillrect(X
,Y
,1,11);
261 fltk::fillrect(X
+W
-2,Y
,1,11);
265 mevent
* e
= cur_seqpat
->p
->events
->next
;
270 if(e
->type
== MIDI_NOTE_ON
){
271 //fltk::fillrect(tick2xpix(e->tick),note2ypix(e->value),e->dur,11);
272 int X
= tick2xpix(e
->tick
) + 1;
273 int Y
= note2ypix(e
->value1
);
274 int W
= tick2xpix(e
->tick
+e
->dur
) - X
;
285 fltk::setcolor(fltk::color(c1
,c2
,c3
));
286 fltk::fillrect(X
+1,Y
+1,W
-1,10);
288 fltk::setcolor(fltk::color(c1
*3/4,c2
*3/4,c3
*3/4));
289 fltk::fillrect(X
,Y
+11,W
,1);
290 fltk::fillrect(X
+W
-1,Y
+1,1,11);
292 fltk::setcolor(fltk::color(c1
*3/4+64,c2
*3/4+64,c3
*3/4+64));
293 fltk::fillrect(X
,Y
,W
,1);
294 fltk::fillrect(X
,Y
,1,11);
302 static int kludge
= 4; //very powerful magic
303 void PianoRoll::layout(){
305 /* the kludge is used so the fltk::ScrollGroup can update
306 widgets not contained within it. Better solution, the
307 scrollgroup could do its callback if it scrolls.
308 Subclassing fltk::ScrollGroup to add this behavior failed. */
315 int W
= tick2xpix(cur_seqpat
->dur
);
319 int wp
= ui
->pattern_scroll
->w();
324 int hp
= ui
->pattern_scroll
->h();
329 int xp
= ui
->pattern_scroll
->xposition();
330 int yp
= ui
->pattern_scroll
->yposition();
334 ui
->pattern_scroll
->scrollTo(xp
,yp
);
337 ui
->pattern_timeline
->scroll
= xp
;
338 ui
->event_edit
->scroll
= xp
;
339 ui
->keyboard
->scroll
= yp
;
342 cur_seqpat
->scrolly
= yp
;
343 cur_seqpat
->scrollx
= xp
;
347 ui
->pattern_timeline
->redraw();
348 ui
->event_edit
->redraw();
351 ui
->keyboard
->redraw();
360 void PianoRoll::load(seqpat
* s
){
362 cur_track
= tracks
[s
->track
];
363 int W
= tick2xpix(s
->dur
);
366 ui
->pattern_timeline
->ticks_offset
= s
->tick
;
369 int PianoRoll::note2ypix(int note
){
370 int udy
= 6*(note
+ (note
+7)/12 + note
/12) + 12;
371 return h() - udy
+ 1;
374 int PianoRoll::tick2xpix(int tick
){
375 return tick
*zoom
*4 / 128;
378 int PianoRoll::xpix2tick(int xpix
){
379 return xpix
*128 / (zoom
*4);
382 int PianoRoll::quantize(int tick
){
383 return tick
/q_tick
* q_tick
;
387 void PianoRoll::set_zoom(int z
){
390 //int W = tick2xpix(cur_seqpat->dur);
395 mevent
* PianoRoll::over_note(){
396 mevent
* e
= cur_seqpat
->p
->events
->next
;
400 if(e
->type
== MIDI_NOTE_ON
){
401 cy
= note2ypix(e
->value1
);
402 lx
= tick2xpix(e
->tick
);
403 rx
= tick2xpix(e
->tick
+e
->dur
);
404 if(event_x() > lx
&& event_x() < rx
&&
405 event_y() < cy
+12 && event_y() > cy
){
415 int PianoRoll::over_handle(mevent
* e
){