Fixed bug in follow playback.
[epichord.git] / src / uihelper.cpp
blobe3557ba95976856809b5e3ff3f6043b46a3390d4
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 <fltk/run.h>
31 #include "seq.h"
32 #include "ui.h"
33 #include "backend.h"
35 #include "uihelper.h"
38 #define CONFIG_FILENAME ".epichordrc"
40 extern UI* ui;
41 extern std::vector<track*> tracks;
43 struct conf config;
45 using namespace std;
47 char* config_filename;
51 void load_config(){
53 //linux dependent
54 char* homepath = getenv("HOME");
55 asprintf(&config_filename,"%s/"CONFIG_FILENAME,homepath);
57 fstream f;
58 f.open(config_filename,fstream::in);
59 if(!f.is_open()){
60 printf("unable to open config file for reading\n");
61 config.beats_per_measure = 4;
62 config.measures_per_phrase = 4;
63 config.measures_until_record = 1;
64 config.alwayscopy = 0;
65 config.autotrackname = 0;
66 config.passthru = 1;
67 config.playinsert = 1;
68 config.recordonchan = 0;
69 config.playmove = 1;
70 config.follow = 1;
71 config.recordmode = 0;
72 config.robmode = 0;
74 load_default_keymap();
75 update_config_gui();
76 return;
79 config.beats_per_measure = 4;
80 config.measures_per_phrase = 4;
82 std::string word;
84 while(!f.eof()){
85 word = "";
86 f >> word;
87 if(word == "leadin"){f>>config.measures_until_record;}
88 else if(word == "alwayscopy"){f>>config.alwayscopy;}
89 else if(word == "autotrackname"){f>>config.autotrackname;}
90 else if(word == "passthru"){f>>config.passthru;}
91 else if(word == "playinsert"){f>>config.playinsert;}
92 else if(word == "recordonchan"){f>>config.recordonchan;}
93 else if(word == "playmove"){f>>config.playmove;}
94 else if(word == "follow"){f>>config.follow;}
95 else if(word == "recordmode"){f>>config.recordmode;}
96 else if(word == "robmode"){f>>config.robmode;}
97 else if(word == "keymap"){load_keymap(f);}
98 else{
99 //read line
102 update_config_gui();
103 f.close();
106 void save_config(){
107 fstream f;
108 f.open(config_filename,fstream::out);
109 if(!f.is_open()){
110 printf("unable to open config file %s for saving\n",config_filename);
111 return;
114 f << "leadin " << config.measures_until_record << endl;
115 f << "alwayscopy " << config.alwayscopy << endl;
116 f << "autotrackname " << config.autotrackname << endl;
117 f << "passthru " << config.passthru << endl;
118 f << "playinsert " << config.playinsert << endl;
119 f << "recordonchan " << config.recordonchan << endl;
120 f << "playmove " << config.playmove << endl;
121 f << "follow " << config.follow << endl;
122 //f << "quantizedur " << config.quantizedur << endl;
123 f << "recordmode " << config.recordmode << endl;
124 f << "robmode " << config.robmode << endl;
125 f << endl;
126 save_keymap(f);
127 f.close();
130 void update_config_gui(){
131 ui->beats_per_measure->value(config.beats_per_measure);
132 ui->measures_per_phrase->value(config.measures_per_phrase);
133 //ui->bpm_wheel.value(config.bpm);
134 //ui->bpm_output.value(config.bpm);
135 ui->measures_until_record->value(config.measures_until_record);
137 ui->check_alwayscopy->state(config.alwayscopy);
138 ui->check_autotrackname->state(config.autotrackname);
139 ui->check_passthru->state(config.passthru);
140 ui->check_playinsert->state(config.playinsert);
141 ui->check_recordonchan->state(config.recordonchan);
142 ui->check_playmove->state(config.playmove);
143 ui->check_follow->state(config.follow);
145 ui->config_window->redraw();
149 void playing_timeout_cb(void* v){
150 //if(!is_backend_playing()){
151 // return;
154 int pos = get_play_position();
155 if(config.follow){
156 ui->arranger->update(pos);
157 ui->piano_roll->update(pos);
159 ui->song_timeline->update(pos);
160 ui->pattern_timeline->update(pos);
161 ui->metronome->update(pos);
163 //check for midi input
164 int tick;
165 int chan;
166 int type;
167 int val1;
168 int val2;
170 track* t = tracks[get_rec_track()];
171 Command* c;
172 seqpat* s;
173 pattern* p;
175 char report[256];
177 while(recv_midi(&chan,&tick,&type,&val1,&val2)){
179 if(config.recordonchan){
180 for(int i=0; i<tracks.size(); i++){
181 if(tracks[i]->chan == chan){
182 t = tracks[i];
187 switch(type){
188 case 0x80://note off
189 snprintf(report,256,"%02x %02x %02x : note off - ch %d note %d vel %d\n",type|chan,val1,val2,chan,val1,val2);
190 scope_print(report);
192 if(!is_backend_recording())
193 break;
195 s = tfind<seqpat>(t->head,tick);
196 if(s->tick+s->dur < tick){
197 //printf("rec head outside block\n");
198 continue;
200 p = s->p;
201 c=new CreateNoteOff(p,val1,val2,tick-s->tick);
202 set_undo(c);
203 undo_push(1);
204 if(ui->piano_roll->visible()){
205 ui->piano_roll->redraw();
206 ui->event_edit->redraw();
207 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[1]=1;}
208 ui->event_menu->redraw();
210 if(ui->arranger->visible())
211 ui->arranger->redraw();
212 break;
213 case 0x90://note on
214 snprintf(report,256,"%02x %02x %02x : note on - ch %d note %d vel %d\n",type|chan,val1,val2,chan,val1,val2);
215 scope_print(report);
217 if(!is_backend_recording())
218 break;
220 s = tfind<seqpat>(t->head,tick);
221 if(s->tick+s->dur < tick){
222 //printf("rec head outside block\n");
223 continue;
225 p = s->p;
226 c=new CreateNoteOn(p,val1,val2,tick-s->tick,16);
227 set_undo(c);
228 undo_push(1);
229 if(ui->piano_roll->visible()){
230 ui->piano_roll->redraw();
231 ui->event_edit->redraw();
232 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[0]=1;}
233 ui->event_menu->redraw();
235 if(ui->arranger->visible())
236 ui->arranger->redraw();
237 break;
238 case 0xa0://aftertouch
239 case 0xb0://controller
240 case 0xc0://program change
241 case 0xd0://channel pressure
242 case 0xe0://pitch wheel
244 s = tfind<seqpat>(t->head,tick);
245 if(s->tick+s->dur < tick){
246 //scope_print("record head outside block\n");
247 continue;
250 switch(type){
251 case 0xa0:
252 snprintf(report,256,"%02x %02x %02x : aftertouch - ch %d note %d %d\n",type|chan,val1,val2,chan,val1,val2);
253 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[2]=1;}
254 break;
255 case 0xb0:
256 snprintf(report,256,"%02x %02x %02x : controller change - ch %d cntr %d val %d\n",type|chan,val1,val2,chan,val1,val2);
257 if(ui->event_edit->cur_seqpat == s){
258 ui->event_edit->has[val1+6]=1;
260 break;
261 case 0xc0:
262 snprintf(report,256,"%02x %02x : program change - ch %d pgrm %d \n",type|chan,val1,chan,val1);
263 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[3]=1;}
264 break;
265 case 0xd0:
266 snprintf(report,256,"%02x %02x : channel pressure - ch %d val %d \n",type|chan,val1,chan,val1);
267 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[4]=1;}
268 break;
269 case 0xe0:
270 snprintf(report,256,"%02x %02x %02x : pitch wheel - ch %d val %d \n",type|chan,val1,val2,chan,(val2<<7)|val1);
271 if(ui->event_edit->cur_seqpat == s){ui->event_edit->has[5]=1;}
272 break;
274 scope_print(report);
276 if(!is_backend_recording())
277 break;
280 p = s->p;
281 c=new CreateEvent(p,type,tick,val1,val2);
282 set_undo(c);
283 undo_push(1);
284 if(ui->piano_roll->visible()){
285 ui->piano_roll->redraw();
286 ui->event_edit->redraw();
287 ui->event_menu->redraw();
289 if(ui->arranger->visible())
290 ui->arranger->redraw();
291 break;
292 case 0xf0:
293 switch(chan){
294 case 1://undefined (reserved) system common message
295 snprintf(report,256,"%02x : undefined (reserved) system common message\n",type|chan);
296 break;
297 case 2://song position pointer
298 snprintf(report,256,"%02x %02x %02x : song position - %d \n",type|chan,val1,val2,(val2<<7)|val1);
299 break;
300 case 3://song select
301 snprintf(report,256,"%02x %02x : song select - %d \n",type|chan,val1,val1);
302 break;
303 case 4://undefined (reserved) system common message
304 case 5://undefined (reserved) system common message
305 snprintf(report,256,"%02x : undefined (reserved) system common message\n",type|chan);
306 break;
307 case 6://tune request
308 snprintf(report,256,"%02x : tune request\n",type|chan);
309 break;
310 case 7://end of exclusive
311 snprintf(report,256,"%02x : end of exclusive\n",type|chan);
312 break;
313 case 8://timing clock
314 snprintf(report,256,"%02x : timing clock\n",type|chan);
315 break;
316 case 9://undefined (reserved) system common message
317 snprintf(report,256,"%02x : undefined (reserved) system common message\n",type|chan);
318 break;
319 case 10://start
320 snprintf(report,256,"%02x : start\n",type|chan);
321 break;
322 case 11://continue
323 snprintf(report,256,"%02x : continue\n",type|chan);
324 break;
325 case 12://stop
326 snprintf(report,256,"%02x : stop\n",type|chan);
327 break;
328 case 13://undefined
329 snprintf(report,256,"%02x : undefined (reserved) system common message\n",type|chan);
330 break;
331 case 14://active sensing
332 snprintf(report,256,"%02x : active sensing\n",type|chan);
333 break;
334 case 15://reset
335 snprintf(report,256,"%02x : reset\n",type|chan);
336 break;
338 if(chan==0){
339 snprintf(report,256,"%02x %02x : system exclusive - id %d ; data follows\n",type|chan,val1,val1);
340 scope_print(report);
341 scope_print((char*)getsysexbuf());
342 scope_print("\nf7 : end of sysex\n");
344 else{
345 scope_print(report);
352 //handle session events (LASH)
353 int ret;
354 char* session_string;
355 char* filename_string;
356 ret=backend_session_process();
357 while(ret != SESSION_NOMORE){
358 session_string=get_session_string();
359 filename_string = (char*)malloc(strlen(session_string)+16);
360 strcpy(filename_string,session_string);
361 strcat(filename_string,"/song.epi");
362 switch(ret){
363 case SESSION_SAVE: save(filename_string); break;
364 case SESSION_LOAD: load(filename_string); break;
365 case SESSION_QUIT: ui->main_window->hide(); break;
366 case SESSION_UNHANDLED: break;
368 free(session_string);
369 ret=backend_session_process();
373 if(is_backend_playing()){
374 fltk::repeat_timeout(0.005, playing_timeout_cb, NULL);
376 else{
377 fltk::repeat_timeout(0.1, playing_timeout_cb, NULL);
381 void start_monitor(){
382 fltk::add_timeout(0.1, playing_timeout_cb, NULL);
385 void press_play(){
386 if(!is_backend_playing()){
387 start_backend();
388 ui->play_button->label("@||");
389 //fltk::add_timeout(0.01, playing_timeout_cb, NULL);
391 else{
392 pause_backend();
393 all_notes_off();
394 ui->play_button->label("@>");
398 void press_stop(){
400 //stops playback and sets the play position to zero
401 pause_backend();
402 reset_backend(0);
403 all_notes_off();
405 ui->song_timeline->update(0);
406 ui->pattern_timeline->update(0);
408 ui->song_timeline->redraw();
409 ui->pattern_timeline->redraw();
411 //send program change on all channels
412 //char buf[3];
413 //for(int i = 0; i<tracks.size(); i++){
414 // program_change(i, tracks[i]->program);
417 ui->play_button->label("@>");
418 ui->play_button->redraw();
420 ui->metronome->update(0);
425 void set_quant(int q){
426 switch(q){
427 case 0:
428 ui->qbutton4->state(0);
429 ui->qbutton8->state(0);
430 ui->qbutton16->state(0);
431 ui->qbutton32->state(0);
432 ui->qbutton64->state(0);
433 ui->qbutton128->state(0);
434 ui->qbutton0->state(1);
435 ui->piano_roll->set_qtick(1);
436 break;
437 case 4:
438 ui->qbutton4->state(1);
439 ui->qbutton8->state(0);
440 ui->qbutton16->state(0);
441 ui->qbutton32->state(0);
442 ui->qbutton64->state(0);
443 ui->qbutton128->state(0);
444 ui->qbutton0->state(0);
445 ui->piano_roll->set_qtick(128);
446 break;
447 case 8:
448 ui->qbutton4->state(0);
449 ui->qbutton8->state(1);
450 ui->qbutton16->state(0);
451 ui->qbutton32->state(0);
452 ui->qbutton64->state(0);
453 ui->qbutton128->state(0);
454 ui->qbutton0->state(0);
455 ui->piano_roll->set_qtick(64);
456 break;
457 case 16:
458 ui->qbutton4->state(0);
459 ui->qbutton8->state(0);
460 ui->qbutton16->state(1);
461 ui->qbutton32->state(0);
462 ui->qbutton64->state(0);
463 ui->qbutton128->state(0);
464 ui->qbutton0->state(0);
465 ui->piano_roll->set_qtick(32);
466 break;
467 case 32:
468 ui->qbutton4->state(0);
469 ui->qbutton8->state(0);
470 ui->qbutton16->state(0);
471 ui->qbutton32->state(1);
472 ui->qbutton64->state(0);
473 ui->qbutton128->state(0);
474 ui->qbutton0->state(0);
475 ui->piano_roll->set_qtick(16);
476 break;
477 case 64:
478 ui->qbutton4->state(0);
479 ui->qbutton8->state(0);
480 ui->qbutton16->state(0);
481 ui->qbutton32->state(0);
482 ui->qbutton64->state(1);
483 ui->qbutton128->state(0);
484 ui->qbutton0->state(0);
485 ui->piano_roll->set_qtick(8);
486 break;
487 case 128:
488 ui->qbutton4->state(0);
489 ui->qbutton8->state(0);
490 ui->qbutton16->state(0);
491 ui->qbutton32->state(0);
492 ui->qbutton64->state(0);
493 ui->qbutton128->state(1);
494 ui->qbutton0->state(0);
495 ui->piano_roll->set_qtick(4);
496 break;
501 void set_beats_per_measure(int n){
502 config.beats_per_measure = n;
503 ui->metronome->set_N(n);
504 ui->piano_roll->redraw();
505 ui->arranger->redraw();
506 ui->arranger->q_tick = n*TICKS_PER_BEAT;
507 ui->song_timeline->redraw();
508 ui->pattern_timeline->redraw();
511 void set_measures_per_phrase(int n){
512 config.measures_per_phrase = n;
513 ui->piano_roll->redraw();
514 ui->arranger->redraw();
515 ui->song_timeline->redraw();
516 ui->pattern_timeline->redraw();
519 void set_measures_until_record(int n){
520 config.measures_until_record = n;
523 void set_alwayscopy(int v){
524 config.alwayscopy = v;
527 void set_autotrackname(int v){
528 config.autotrackname = v;
531 void set_passthru(int v){
532 config.passthru = v;
533 backend_set_passthru(v);
536 void set_playinsert(int v){
537 config.playinsert = v;
540 void set_recordonchan(int v){
541 config.recordonchan = v;
544 void set_playmove(int v){
545 config.playmove = v;
548 void set_follow(int v){
549 config.follow = v;
552 void set_recordmode(int n){
553 config.recordmode = n;
556 void set_robmode(int n){
557 config.robmode = n;
561 void scope_print(char* text){
562 ui->scope->append(text);
563 int N = ui->scope->buffer()->length();
564 ui->scope->scroll(N,0);
569 void show_song_edit(){
570 ui->pattern_edit->hide();
571 ui->pattern_buttons->hide();
572 ui->song_edit->activate();
573 ui->song_edit->show();
574 ui->song_edit->take_focus();
575 ui->song_buttons->show();
578 void show_pattern_edit(){
579 ui->song_edit->hide();
580 ui->song_edit->deactivate();
581 ui->song_buttons->hide();
582 ui->pattern_edit->take_focus();
583 ui->pattern_edit->show();
584 ui->pattern_buttons->show();