Added missing headers, again.
[epichord.git] / src / uihelper.cpp
blobc8c99119afa362dc58065ed403be46078919e7bb
1 /*
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
23 #include <stdlib.h>
24 #include <vector>
25 #include <fstream>
26 #include <string.h>
27 #include <math.h>
29 #include <limits>
31 #include <fltk/run.h>
33 #include "seq.h"
34 #include "ui.h"
35 #include "backend.h"
37 #include "uihelper.h"
40 #define CONFIG_FILENAME ".epichordrc"
42 extern UI* ui;
43 extern std::vector<track*> tracks;
45 struct conf config;
47 using namespace std;
49 char* config_filename;
53 void load_config(){
55 //linux dependent
56 char* homepath = getenv("HOME");
57 asprintf(&config_filename,"%s/"CONFIG_FILENAME,homepath);
59 fstream f;
60 f.open(config_filename,fstream::in);
61 if(!f.is_open()){
62 printf("load_config: Unable to open config file for reading.\n");
63 config.beats_per_measure = 4;
64 config.measures_per_phrase = 4;
65 config.measures_until_record = 1;
66 config.alwayscopy = 0;
67 config.autotrackname = 0;
68 config.passthru = 1;
69 config.playinsert = 1;
70 config.recordonchan = 0;
71 config.playmove = 1;
72 config.follow = 1;
73 config.recordmode = 0;
74 config.robmode = 0;
75 config.defaultvelocity = 96;
77 load_default_keymap();
78 update_config_gui();
79 return;
82 config.beats_per_measure = 4;
83 config.measures_per_phrase = 4;
85 std::string word;
87 while(!f.eof()){
88 word = "";
89 f >> word;
90 if(word == "leadin"){f>>config.measures_until_record;}
91 else if(word == "alwayscopy"){f>>config.alwayscopy;}
92 else if(word == "autotrackname"){f>>config.autotrackname;}
93 else if(word == "passthru"){f>>config.passthru;}
94 else if(word == "playinsert"){f>>config.playinsert;}
95 else if(word == "recordonchan"){f>>config.recordonchan;}
96 else if(word == "playmove"){f>>config.playmove;}
97 else if(word == "follow"){f>>config.follow;}
98 else if(word == "recordmode"){f>>config.recordmode;}
99 else if(word == "robmode"){f>>config.robmode;}
100 else if(word == "keymap"){load_keymap(f);}
101 else if(word == "defaultvelocity"){f>>config.defaultvelocity;}
102 else{
103 f.ignore(std::numeric_limits<streamsize>::max(),'\n');
106 update_config_gui();
107 f.close();
110 void save_config(){
111 fstream f;
112 f.open(config_filename,fstream::out);
113 if(!f.is_open()){
114 printf("save_config: Unable to open config file %s for saving.\n", config_filename);
115 return;
118 f << "leadin " << config.measures_until_record << endl;
119 f << "alwayscopy " << config.alwayscopy << endl;
120 f << "autotrackname " << config.autotrackname << endl;
121 f << "passthru " << config.passthru << endl;
122 f << "playinsert " << config.playinsert << endl;
123 f << "recordonchan " << config.recordonchan << endl;
124 f << "playmove " << config.playmove << endl;
125 f << "follow " << config.follow << endl;
126 //f << "quantizedur " << config.quantizedur << endl;
127 f << "recordmode " << config.recordmode << endl;
128 f << "robmode " << config.robmode << endl;
129 f << "defaultvelocity " << config.defaultvelocity << endl;
130 f << endl;
131 save_keymap(f);
132 f.close();
135 void update_config_gui(){
136 ui->beats_per_measure->value(config.beats_per_measure);
137 ui->measures_per_phrase->value(config.measures_per_phrase);
138 ui->measures_until_record->value(config.measures_until_record);
140 ui->check_alwayscopy->state(config.alwayscopy);
141 ui->check_autotrackname->state(config.autotrackname);
142 ui->check_passthru->state(config.passthru);
143 ui->check_playinsert->state(config.playinsert);
144 ui->check_recordonchan->state(config.recordonchan);
145 ui->check_playmove->state(config.playmove);
146 ui->check_follow->state(config.follow);
148 ui->menu_recordmode->value(config.recordmode);
149 ui->menu_rob->value(config.robmode);
151 ui->default_velocity->value(config.defaultvelocity);
153 ui->config_window->redraw();
158 seqpat* rob_check(seqpat* s){
159 seqpat* prev = s->prev;
160 Command* c;
161 if(config.robmode == 0){
162 return NULL;
164 else if(config.robmode == 1 || prev == NULL){
165 int pos = get_play_position();
166 int M = config.measures_per_phrase*config.beats_per_measure*128;
167 int P1 = pos/M*M;
168 int P2 = P1 + M;
169 int T = P1;
170 int R = s->tick+s->dur;
171 if(R > P1){
172 T = R;
174 int W = P2 - T;
175 if(s->next){
176 int L = s->next->tick;
177 if(L < P2){
178 W = L - T;
181 c = new CreateSeqpatBlank(s->track,T,W);
182 set_undo(c);
183 undo_push(1);
184 return s->next;
186 else if(config.robmode == 2){
187 int pos = get_play_position();
188 int M = config.measures_per_phrase*config.beats_per_measure*128;
189 int P = pos/M*M + M;//tick at next phrase boundary
190 int W = P - s->tick;
191 if(s->next){
192 int W2 = s->next->tick - s->tick;
193 if(W2 < W){
194 W=W2;
197 c = new ResizeSeqpat(s,W);
198 set_undo(c);
199 undo_push(1);
200 return prev->next;
206 int last_pos=0;
207 void playing_timeout_cb(void* v){
208 int pos = get_play_position();
210 if(pos < last_pos){
211 reset_record_flags();
213 last_pos = pos;
215 if(config.follow){
216 ui->arranger->update(pos);
217 ui->piano_roll->update(pos);
219 ui->song_timeline->update(pos);
220 ui->pattern_timeline->update(pos);
221 ui->metronome->update(pos);
223 //check for midi input
224 int tick;
225 int chan;
226 int type;
227 int val1;
228 int val2;
230 track* t = tracks[get_rec_track()];
231 Command* c;
232 seqpat* s;
233 pattern* p;
235 char report[256];
237 while(recv_midi(&chan,&tick,&type,&val1,&val2)){
239 if(config.recordonchan){
240 for(int i=0; i<tracks.size(); i++){
241 if(tracks[i]->chan == chan){
242 t = tracks[i];
247 switch(type){
248 case 0x80://note off
249 snprintf(report,256,"%02x %02x %02x : note off - ch %d note %d vel %d\n",type|chan,val1,val2,chan,val1,val2);
250 scope_print(report);
252 if(!is_backend_recording())
253 break;
255 s = tfind<seqpat>(t->head,tick);
256 if(s->tick+s->dur < tick){
257 s = rob_check(s);
258 if(!s){continue;}
261 //if(s->record_flag==1 && config.recordmode>0){show_song_edit();}
262 s->record_check(config.recordmode);
263 p = s->p;
264 c=new CreateNoteOff(p,val1,val2,tick-s->tick);
265 set_undo(c);
266 undo_push(1);
267 if(ui->piano_roll->visible()){
268 ui->piano_roll->redraw();
269 ui->event_edit->redraw();
270 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[1]=1;}
271 ui->event_menu->redraw();
273 if(ui->arranger->visible())
274 ui->arranger->redraw();
275 break;
276 case 0x90://note on
277 snprintf(report,256,"%02x %02x %02x : note on - ch %d note %d vel %d\n",type|chan,val1,val2,chan,val1,val2);
278 scope_print(report);
280 if(!is_backend_recording())
281 break;
283 s = tfind<seqpat>(t->head,tick);
284 if(s->tick+s->dur < tick){
285 s = rob_check(s);
286 if(!s){continue;}
289 // if(s->record_flag==1 && config.recordmode>0){show_song_edit();}
290 s->record_check(config.recordmode);
291 p = s->p;
292 c=new CreateNoteOn(p,val1,val2,tick-s->tick,16);
293 set_undo(c);
294 undo_push(1);
295 if(ui->piano_roll->visible()){
296 ui->piano_roll->redraw();
297 ui->event_edit->redraw();
298 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[0]=1;}
299 ui->event_menu->redraw();
301 if(ui->arranger->visible())
302 ui->arranger->redraw();
303 break;
304 case 0xa0://aftertouch
305 case 0xb0://controller
306 case 0xc0://program change
307 case 0xd0://channel pressure
308 case 0xe0://pitch wheel
310 s = tfind<seqpat>(t->head,tick);
311 if(s->tick+s->dur < tick){
312 s = rob_check(s);
313 if(!s){continue;}
316 switch(type){
317 case 0xa0:
318 snprintf(report,256,"%02x %02x %02x : aftertouch - ch %d note %d %d\n",type|chan,val1,val2,chan,val1,val2);
319 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[2]=1;}
320 break;
321 case 0xb0:
322 snprintf(report,256,"%02x %02x %02x : controller change - ch %d cntr %d val %d\n",type|chan,val1,val2,chan,val1,val2);
323 if(ui->event_edit->cur_seqpat == s){
324 ui->event_edit->has[val1+6]=1;
326 break;
327 case 0xc0:
328 snprintf(report,256,"%02x %02x : program change - ch %d pgrm %d \n",type|chan,val1,chan,val1);
329 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[3]=1;}
330 break;
331 case 0xd0:
332 snprintf(report,256,"%02x %02x : channel pressure - ch %d val %d \n",type|chan,val1,chan,val1);
333 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[4]=1;}
334 break;
335 case 0xe0:
336 snprintf(report,256,"%02x %02x %02x : pitch wheel - ch %d val %d \n",type|chan,val1,val2,chan,(val2<<7)|val1);
337 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[5]=1;}
338 break;
340 scope_print(report);
342 if(!is_backend_recording())
343 break;
345 // if(s->record_flag==1 && config.recordmode>0){show_song_edit();}
346 s->record_check(config.recordmode);
347 p = s->p;
348 c=new CreateEvent(p,type,tick,val1,val2);
349 set_undo(c);
350 undo_push(1);
351 if(ui->piano_roll->visible()){
352 ui->piano_roll->redraw();
353 ui->event_edit->redraw();
354 ui->event_menu->redraw();
356 if(ui->arranger->visible())
357 ui->arranger->redraw();
358 break;
359 case 0xf0:
360 switch(chan){
361 case 1://undefined (reserved) system common message
362 snprintf(report,256,"%02x : undefined (reserved) system common message\n",type|chan);
363 break;
364 case 2://song position pointer
365 snprintf(report,256,"%02x %02x %02x : song position - %d \n",type|chan,val1,val2,(val2<<7)|val1);
366 break;
367 case 3://song select
368 snprintf(report,256,"%02x %02x : song select - %d \n",type|chan,val1,val1);
369 break;
370 case 4://undefined (reserved) system common message
371 case 5://undefined (reserved) system common message
372 snprintf(report,256,"%02x : undefined (reserved) system common message\n",type|chan);
373 break;
374 case 6://tune request
375 snprintf(report,256,"%02x : tune request\n",type|chan);
376 break;
377 case 7://end of exclusive
378 snprintf(report,256,"%02x : end of exclusive\n",type|chan);
379 break;
380 case 8://timing clock
381 snprintf(report,256,"%02x : timing clock\n",type|chan);
382 break;
383 case 9://undefined (reserved) system common message
384 snprintf(report,256,"%02x : undefined (reserved) system common message\n",type|chan);
385 break;
386 case 10://start
387 snprintf(report,256,"%02x : start\n",type|chan);
388 break;
389 case 11://continue
390 snprintf(report,256,"%02x : continue\n",type|chan);
391 break;
392 case 12://stop
393 snprintf(report,256,"%02x : stop\n",type|chan);
394 break;
395 case 13://undefined
396 snprintf(report,256,"%02x : undefined (reserved) system common message\n",type|chan);
397 break;
398 case 14://active sensing
399 snprintf(report,256,"%02x : active sensing\n",type|chan);
400 break;
401 case 15://reset
402 snprintf(report,256,"%02x : reset\n",type|chan);
403 break;
405 if(chan==0){
406 snprintf(report,256,"%02x %02x : system exclusive - id %d ; data follows\n",type|chan,val1,val1);
407 scope_print(report);
408 scope_print(getsysexbuf());
409 scope_print("\nf7 : end of sysex\n");
411 else{
412 scope_print(report);
419 //handle session events (LASH)
420 int ret;
421 char* session_string;
422 char* filename_string;
423 ret=backend_session_process();
424 while(ret != SESSION_NOMORE){
425 session_string=get_session_string();
426 filename_string = (char*)malloc(strlen(session_string)+16);
427 strcpy(filename_string,session_string);
428 strcat(filename_string,"/song.epi");
429 switch(ret){
430 case SESSION_SAVE: save(filename_string); break;
431 case SESSION_LOAD: load(filename_string); break;
432 case SESSION_QUIT: ui->main_window->hide(); break;
433 case SESSION_UNHANDLED: break;
435 free(session_string);
436 ret=backend_session_process();
440 if(is_backend_playing()){
441 fltk::repeat_timeout(0.005, playing_timeout_cb, NULL);
443 else{
444 fltk::repeat_timeout(0.1, playing_timeout_cb, NULL);
448 void start_monitor(){
449 fltk::add_timeout(0.1, playing_timeout_cb, NULL);
452 void press_play(){
453 if(!is_backend_playing()){
454 start_backend();
455 ui->play_button->label("@||");
456 //fltk::add_timeout(0.01, playing_timeout_cb, NULL);
458 else{
459 pause_backend();
460 all_notes_off();
461 ui->play_button->label("@>");
465 void press_stop(){
467 //stops playback and sets the play position to zero
468 pause_backend();
469 reset_backend(0);
470 all_notes_off();
472 ui->song_timeline->update(0);
473 ui->pattern_timeline->update(0);
475 ui->song_timeline->redraw();
476 ui->pattern_timeline->redraw();
478 ui->play_button->label("@>");
479 ui->play_button->redraw();
481 ui->metronome->update(0);
486 void set_quant(int q){
487 switch(q){
488 case 0:
489 ui->qbutton4->state(0);
490 ui->qbutton8->state(0);
491 ui->qbutton16->state(0);
492 ui->qbutton32->state(0);
493 ui->qbutton64->state(0);
494 ui->qbutton128->state(0);
495 ui->qbutton0->state(1);
496 ui->piano_roll->set_qtick(1);
497 break;
498 case 4:
499 ui->qbutton4->state(1);
500 ui->qbutton8->state(0);
501 ui->qbutton16->state(0);
502 ui->qbutton32->state(0);
503 ui->qbutton64->state(0);
504 ui->qbutton128->state(0);
505 ui->qbutton0->state(0);
506 ui->piano_roll->set_qtick(128);
507 break;
508 case 8:
509 ui->qbutton4->state(0);
510 ui->qbutton8->state(1);
511 ui->qbutton16->state(0);
512 ui->qbutton32->state(0);
513 ui->qbutton64->state(0);
514 ui->qbutton128->state(0);
515 ui->qbutton0->state(0);
516 ui->piano_roll->set_qtick(64);
517 break;
518 case 16:
519 ui->qbutton4->state(0);
520 ui->qbutton8->state(0);
521 ui->qbutton16->state(1);
522 ui->qbutton32->state(0);
523 ui->qbutton64->state(0);
524 ui->qbutton128->state(0);
525 ui->qbutton0->state(0);
526 ui->piano_roll->set_qtick(32);
527 break;
528 case 32:
529 ui->qbutton4->state(0);
530 ui->qbutton8->state(0);
531 ui->qbutton16->state(0);
532 ui->qbutton32->state(1);
533 ui->qbutton64->state(0);
534 ui->qbutton128->state(0);
535 ui->qbutton0->state(0);
536 ui->piano_roll->set_qtick(16);
537 break;
538 case 64:
539 ui->qbutton4->state(0);
540 ui->qbutton8->state(0);
541 ui->qbutton16->state(0);
542 ui->qbutton32->state(0);
543 ui->qbutton64->state(1);
544 ui->qbutton128->state(0);
545 ui->qbutton0->state(0);
546 ui->piano_roll->set_qtick(8);
547 break;
548 case 128:
549 ui->qbutton4->state(0);
550 ui->qbutton8->state(0);
551 ui->qbutton16->state(0);
552 ui->qbutton32->state(0);
553 ui->qbutton64->state(0);
554 ui->qbutton128->state(1);
555 ui->qbutton0->state(0);
556 ui->piano_roll->set_qtick(4);
557 break;
562 void set_beats_per_measure(int n){
563 config.beats_per_measure = n;
564 ui->metronome->set_N(n);
565 ui->piano_roll->redraw();
566 ui->arranger->redraw();
567 ui->arranger->q_tick = n*TICKS_PER_BEAT;
568 ui->song_timeline->redraw();
569 ui->pattern_timeline->redraw();
572 void set_measures_per_phrase(int n){
573 config.measures_per_phrase = n;
574 ui->piano_roll->redraw();
575 ui->arranger->redraw();
576 ui->song_timeline->redraw();
577 ui->pattern_timeline->redraw();
580 void set_measures_until_record(int n){
581 config.measures_until_record = n;
584 void set_alwayscopy(int v){
585 config.alwayscopy = v;
588 void set_autotrackname(int v){
589 config.autotrackname = v;
592 void set_passthru(int v){
593 config.passthru = v;
594 backend_set_passthru(v);
597 void set_playinsert(int v){
598 config.playinsert = v;
601 void set_recordonchan(int v){
602 config.recordonchan = v;
605 void set_playmove(int v){
606 config.playmove = v;
609 void set_follow(int v){
610 config.follow = v;
613 void set_recordmode(int n){
614 config.recordmode = n;
617 void set_robmode(int n){
618 config.robmode = n;
621 void set_defaultvelocity(int n){
622 config.defaultvelocity = n;
626 int scopeon=0;
627 void turnonscope(){
628 scopeon=1;
631 void turnoffscope(){
632 scopeon=0;
633 fltk::TextBuffer* ptr = ui->scope->buffer();
634 ptr->remove(0,ptr->length());
635 // ui->redraw();
638 void scope_print(const char* text){
639 if(scopeon){
640 ui->scope->append(text);
641 int N = ui->scope->buffer()->length();
642 ui->scope->scroll(N,0);
648 void show_song_edit(){
649 ui->pattern_edit->hide();
650 ui->pattern_buttons->hide();
651 ui->song_edit->activate();
652 ui->song_edit->show();
653 ui->song_edit->take_focus();
654 ui->song_buttons->show();
657 void show_pattern_edit(){
658 ui->song_edit->hide();
659 ui->song_edit->deactivate();
660 ui->song_buttons->hide();
661 ui->pattern_edit->take_focus();
662 ui->pattern_edit->show();
663 ui->pattern_buttons->show();
667 static int tool = 0;
668 //switch between normal, note off, portamento, and aftertouch
669 void toggle_tool(){
670 switch(tool){
671 case 0:
672 tool=1;
673 ui->tool_button->copy_label("80");
674 ui->tool_button->state(1);
675 break;
676 case 1:
677 tool=2;
678 ui->tool_button->copy_label("A0");
679 break;
680 case 2:
681 tool=3;
682 ui->tool_button->copy_label("po");
683 break;
684 case 3:
685 tool=0;
686 ui->tool_button->copy_label("tool");
687 ui->tool_button->state(0);
688 break;