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
40 #define CONFIG_FILENAME ".epichordrc"
43 extern std::vector
<track
*> tracks
;
49 char* config_filename
;
55 config
.beats_per_measure
= 4;
56 config
.measures_per_phrase
= 4;
57 config
.measures_until_record
= 1;
58 config
.alwayscopy
= 0;
59 config
.autotrackname
= 0;
61 config
.playinsert
= 1;
62 config
.recordonchan
= 0;
65 config
.recordmode
= 0;
67 config
.defaultvelocity
= 96;
71 char* homepath
= getenv("HOME");
72 asprintf(&config_filename
,"%s/"CONFIG_FILENAME
,homepath
);
75 f
.open(config_filename
,fstream::in
);
77 printf("load_config: Unable to open config file for reading.\n");
79 load_default_keymap();
84 config
.beats_per_measure
= 4;
85 config
.measures_per_phrase
= 4;
92 if(word
== "leadin"){f
>>config
.measures_until_record
;}
93 else if(word
== "alwayscopy"){f
>>config
.alwayscopy
;}
94 else if(word
== "autotrackname"){f
>>config
.autotrackname
;}
95 else if(word
== "passthru"){f
>>config
.passthru
;}
96 else if(word
== "playinsert"){f
>>config
.playinsert
;}
97 else if(word
== "recordonchan"){f
>>config
.recordonchan
;}
98 else if(word
== "playmove"){f
>>config
.playmove
;}
99 else if(word
== "follow"){f
>>config
.follow
;}
100 else if(word
== "recordmode"){f
>>config
.recordmode
;}
101 else if(word
== "robmode"){f
>>config
.robmode
;}
102 else if(word
== "keymap"){load_keymap(f
);}
103 else if(word
== "defaultvelocity"){f
>>config
.defaultvelocity
;}
104 else if(word
== "trackinit"){f
>>config
.trackinit
;}
106 f
.ignore(std::numeric_limits
<streamsize
>::max(),'\n');
110 backend_set_trackinit(config
.trackinit
);
118 f
.open(config_filename
,fstream::out
);
120 printf("save_config: Unable to open config file %s for saving.\n", config_filename
);
124 f
<< "leadin " << config
.measures_until_record
<< endl
;
125 f
<< "alwayscopy " << config
.alwayscopy
<< endl
;
126 f
<< "autotrackname " << config
.autotrackname
<< endl
;
127 f
<< "passthru " << config
.passthru
<< endl
;
128 f
<< "playinsert " << config
.playinsert
<< endl
;
129 f
<< "recordonchan " << config
.recordonchan
<< endl
;
130 f
<< "playmove " << config
.playmove
<< endl
;
131 f
<< "follow " << config
.follow
<< endl
;
132 //f << "quantizedur " << config.quantizedur << endl;
133 f
<< "recordmode " << config
.recordmode
<< endl
;
134 f
<< "robmode " << config
.robmode
<< endl
;
135 f
<< "defaultvelocity " << config
.defaultvelocity
<< endl
;
136 f
<< "trackinit " << config
.trackinit
<< endl
;
142 void update_config_gui(){
143 ui
->beats_per_measure
->value(config
.beats_per_measure
);
144 ui
->measures_per_phrase
->value(config
.measures_per_phrase
);
145 ui
->measures_until_record
->value(config
.measures_until_record
);
147 ui
->bpm_wheel
->value(config
.beats_per_minute
);
148 ui
->bpm_output
->value(config
.beats_per_minute
);
150 ui
->check_alwayscopy
->state(config
.alwayscopy
);
151 ui
->check_autotrackname
->state(config
.autotrackname
);
152 ui
->check_passthru
->state(config
.passthru
);
153 ui
->check_playinsert
->state(config
.playinsert
);
154 ui
->check_recordonchan
->state(config
.recordonchan
);
155 ui
->check_playmove
->state(config
.playmove
);
156 ui
->check_follow
->state(config
.follow
);
158 ui
->menu_recordmode
->value(config
.recordmode
);
159 ui
->menu_rob
->value(config
.robmode
);
161 ui
->default_velocity
->value(config
.defaultvelocity
);
163 ui
->check_trackinit
->value(config
.trackinit
);
165 ui
->config_window
->redraw();
170 seqpat
* rob_check(seqpat
* s
){
171 seqpat
* prev
= s
->prev
;
173 if(config
.robmode
== 0){
176 else if(config
.robmode
== 1 || prev
== NULL
){
177 int pos
= get_play_position();
178 int M
= config
.measures_per_phrase
;
180 M
= M
*config
.beats_per_measure
*128;
183 M
= 4*config
.beats_per_measure
*128;
188 int R
= s
->tick
+s
->dur
;
194 int L
= s
->next
->tick
;
199 c
= new CreateSeqpatBlank(s
->track
,T
,W
);
204 else if(config
.robmode
== 2){
205 int pos
= get_play_position();
206 int M
= config
.measures_per_phrase
;
208 M
= M
*config
.beats_per_measure
*128;
211 M
= 4*config
.beats_per_measure
*128;
213 int P
= pos
/M
*M
+ M
;//tick at next phrase boundary
216 int W2
= s
->next
->tick
- s
->tick
;
221 c
= new ResizeSeqpat(s
,W
);
231 void playing_timeout_cb(void* v
){
232 int pos
= get_play_position();
235 reset_record_flags();
240 ui
->arranger
->update(pos
);
241 ui
->piano_roll
->update(pos
);
243 ui
->song_timeline
->update(pos
);
244 ui
->pattern_timeline
->update(pos
);
245 ui
->metronome
->update(pos
);
247 //check for midi input
254 track
* t
= tracks
[get_rec_track()];
261 while(recv_midi(&chan
,&tick
,&type
,&val1
,&val2
)){
263 if(config
.recordonchan
){
264 for(int i
=0; i
<tracks
.size(); i
++){
265 if(tracks
[i
]->chan
== chan
){
273 snprintf(report
,256,"%02x %02x %02x : note off - ch %d note %d vel %d\n",type
|chan
,val1
,val2
,chan
,val1
,val2
);
276 if(!is_backend_recording())
279 s
= tfind
<seqpat
>(t
->head
,tick
);
280 if(s
->tick
+s
->dur
< tick
){
285 //if(s->record_flag==1 && config.recordmode>0){show_song_edit();}
286 s
->record_check(config
.recordmode
);
288 c
=new CreateNoteOff(p
,val1
,val2
,tick
-s
->tick
);
291 if(ui
->piano_roll
->visible()){
292 ui
->piano_roll
->redraw();
293 ui
->event_edit
->redraw();
294 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[1]=1;}
295 ui
->event_menu
->redraw();
297 if(ui
->arranger
->visible())
298 ui
->arranger
->redraw();
301 snprintf(report
,256,"%02x %02x %02x : note on - ch %d note %d vel %d\n",type
|chan
,val1
,val2
,chan
,val1
,val2
);
304 if(!is_backend_recording())
307 s
= tfind
<seqpat
>(t
->head
,tick
);
308 if(s
->tick
+s
->dur
< tick
){
313 // if(s->record_flag==1 && config.recordmode>0){show_song_edit();}
314 s
->record_check(config
.recordmode
);
316 c
=new CreateNoteOn(p
,val1
,val2
,tick
-s
->tick
,16);
319 if(ui
->piano_roll
->visible()){
320 ui
->piano_roll
->redraw();
321 ui
->event_edit
->redraw();
322 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[0]=1;}
323 ui
->event_menu
->redraw();
325 if(ui
->arranger
->visible())
326 ui
->arranger
->redraw();
328 case 0xa0://aftertouch
329 case 0xb0://controller
330 case 0xc0://program change
331 case 0xd0://channel pressure
332 case 0xe0://pitch wheel
334 s
= tfind
<seqpat
>(t
->head
,tick
);
335 if(s
->tick
+s
->dur
< tick
){
342 snprintf(report
,256,"%02x %02x %02x : aftertouch - ch %d note %d %d\n",type
|chan
,val1
,val2
,chan
,val1
,val2
);
343 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[2]=1;}
346 snprintf(report
,256,"%02x %02x %02x : controller change - ch %d cntr %d val %d\n",type
|chan
,val1
,val2
,chan
,val1
,val2
);
347 if(ui
->event_edit
->cur_seqpat
== s
){
348 ui
->event_edit
->has
[val1
+6]=1;
352 snprintf(report
,256,"%02x %02x : program change - ch %d pgrm %d \n",type
|chan
,val1
,chan
,val1
);
353 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[3]=1;}
356 snprintf(report
,256,"%02x %02x : channel pressure - ch %d val %d \n",type
|chan
,val1
,chan
,val1
);
357 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[4]=1;}
360 snprintf(report
,256,"%02x %02x %02x : pitch wheel - ch %d val %d \n",type
|chan
,val1
,val2
,chan
,(val2
<<7)|val1
);
361 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[5]=1;}
366 if(!is_backend_recording())
369 // if(s->record_flag==1 && config.recordmode>0){show_song_edit();}
370 s
->record_check(config
.recordmode
);
372 c
=new CreateEvent(p
,type
,tick
,val1
,val2
);
375 if(ui
->piano_roll
->visible()){
376 ui
->piano_roll
->redraw();
377 ui
->event_edit
->redraw();
378 ui
->event_menu
->redraw();
380 if(ui
->arranger
->visible())
381 ui
->arranger
->redraw();
385 case 1://undefined (reserved) system common message
386 snprintf(report
,256,"%02x : undefined (reserved) system common message\n",type
|chan
);
388 case 2://song position pointer
389 snprintf(report
,256,"%02x %02x %02x : song position - %d \n",type
|chan
,val1
,val2
,(val2
<<7)|val1
);
392 snprintf(report
,256,"%02x %02x : song select - %d \n",type
|chan
,val1
,val1
);
394 case 4://undefined (reserved) system common message
395 case 5://undefined (reserved) system common message
396 snprintf(report
,256,"%02x : undefined (reserved) system common message\n",type
|chan
);
398 case 6://tune request
399 snprintf(report
,256,"%02x : tune request\n",type
|chan
);
401 case 7://end of exclusive
402 snprintf(report
,256,"%02x : end of exclusive\n",type
|chan
);
404 case 8://timing clock
405 snprintf(report
,256,"%02x : timing clock\n",type
|chan
);
407 case 9://undefined (reserved) system common message
408 snprintf(report
,256,"%02x : undefined (reserved) system common message\n",type
|chan
);
411 snprintf(report
,256,"%02x : start\n",type
|chan
);
414 snprintf(report
,256,"%02x : continue\n",type
|chan
);
417 snprintf(report
,256,"%02x : stop\n",type
|chan
);
420 snprintf(report
,256,"%02x : undefined (reserved) system common message\n",type
|chan
);
422 case 14://active sensing
423 snprintf(report
,256,"%02x : active sensing\n",type
|chan
);
426 snprintf(report
,256,"%02x : reset\n",type
|chan
);
430 snprintf(report
,256,"%02x %02x : system exclusive - id %d ; data follows\n",type
|chan
,val1
,val1
);
432 scope_print(getsysexbuf());
433 scope_print("\nf7 : end of sysex\n");
443 //handle session events (LASH)
445 char* session_string
;
446 char* filename_string
;
447 ret
=backend_session_process();
448 while(ret
!= SESSION_NOMORE
){
449 session_string
=get_session_string();
450 filename_string
= (char*)malloc(strlen(session_string
)+16);
451 strcpy(filename_string
,session_string
);
452 strcat(filename_string
,"/song.epi");
454 case SESSION_SAVE
: save(filename_string
); break;
455 case SESSION_LOAD
: load(filename_string
); break;
456 case SESSION_QUIT
: shutdown_gui(); break;
457 case SESSION_UNHANDLED
: break;
459 free(session_string
);
460 ret
=backend_session_process();
464 if(is_backend_playing()){
465 fltk::repeat_timeout(0.005, playing_timeout_cb
, NULL
);
468 fltk::repeat_timeout(0.1, playing_timeout_cb
, NULL
);
472 void start_monitor(){
473 fltk::add_timeout(0.1, playing_timeout_cb
, NULL
);
477 if(!is_backend_playing()){
479 ui
->play_button
->label("@||");
480 //fltk::add_timeout(0.01, playing_timeout_cb, NULL);
485 ui
->play_button
->label("@>");
491 int left
= get_loop_start();
492 if(get_play_position()==left
|| get_play_position()==0){
500 ui
->song_timeline
->update(left
);
501 ui
->pattern_timeline
->update(left
);
503 ui
->song_timeline
->redraw();
504 ui
->pattern_timeline
->redraw();
506 ui
->play_button
->label("@>");
507 ui
->play_button
->redraw();
509 ui
->metronome
->update(left
);
514 void set_quant(int q
){
517 ui
->qbutton4
->state(0);
518 ui
->qbutton8
->state(0);
519 ui
->qbutton16
->state(0);
520 ui
->qbutton32
->state(0);
521 ui
->qbutton64
->state(0);
522 ui
->qbutton128
->state(0);
523 ui
->qbutton0
->state(1);
524 ui
->piano_roll
->set_qtick(1);
527 ui
->qbutton4
->state(1);
528 ui
->qbutton8
->state(0);
529 ui
->qbutton16
->state(0);
530 ui
->qbutton32
->state(0);
531 ui
->qbutton64
->state(0);
532 ui
->qbutton128
->state(0);
533 ui
->qbutton0
->state(0);
534 ui
->piano_roll
->set_qtick(128);
537 ui
->qbutton4
->state(0);
538 ui
->qbutton8
->state(1);
539 ui
->qbutton16
->state(0);
540 ui
->qbutton32
->state(0);
541 ui
->qbutton64
->state(0);
542 ui
->qbutton128
->state(0);
543 ui
->qbutton0
->state(0);
544 ui
->piano_roll
->set_qtick(64);
547 ui
->qbutton4
->state(0);
548 ui
->qbutton8
->state(0);
549 ui
->qbutton16
->state(1);
550 ui
->qbutton32
->state(0);
551 ui
->qbutton64
->state(0);
552 ui
->qbutton128
->state(0);
553 ui
->qbutton0
->state(0);
554 ui
->piano_roll
->set_qtick(32);
557 ui
->qbutton4
->state(0);
558 ui
->qbutton8
->state(0);
559 ui
->qbutton16
->state(0);
560 ui
->qbutton32
->state(1);
561 ui
->qbutton64
->state(0);
562 ui
->qbutton128
->state(0);
563 ui
->qbutton0
->state(0);
564 ui
->piano_roll
->set_qtick(16);
567 ui
->qbutton4
->state(0);
568 ui
->qbutton8
->state(0);
569 ui
->qbutton16
->state(0);
570 ui
->qbutton32
->state(0);
571 ui
->qbutton64
->state(1);
572 ui
->qbutton128
->state(0);
573 ui
->qbutton0
->state(0);
574 ui
->piano_roll
->set_qtick(8);
577 ui
->qbutton4
->state(0);
578 ui
->qbutton8
->state(0);
579 ui
->qbutton16
->state(0);
580 ui
->qbutton32
->state(0);
581 ui
->qbutton64
->state(0);
582 ui
->qbutton128
->state(1);
583 ui
->qbutton0
->state(0);
584 ui
->piano_roll
->set_qtick(4);
589 void set_songtool(int i
){
592 ui
->edit_button
->state(1);
593 ui
->color_button
->state(0);
594 ui
->unclone_button
->state(0);
595 ui
->split_button
->state(0);
596 ui
->join_button
->state(0);
597 ui
->arranger
->color_flag
= 0;
598 ui
->arranger
->unclone_flag
= 0;
599 ui
->arranger
->split_flag
= 0;
600 ui
->arranger
->join_flag
= 0;
603 ui
->edit_button
->state(0);
604 ui
->color_button
->state(1);
605 ui
->unclone_button
->state(0);
606 ui
->split_button
->state(0);
607 ui
->join_button
->state(0);
608 ui
->arranger
->color_flag
= 1;
609 ui
->arranger
->unclone_flag
= 0;
610 ui
->arranger
->split_flag
= 0;
611 ui
->arranger
->join_flag
= 0;
614 ui
->edit_button
->state(0);
615 ui
->color_button
->state(0);
616 ui
->unclone_button
->state(1);
617 ui
->split_button
->state(0);
618 ui
->join_button
->state(0);
619 ui
->arranger
->color_flag
= 0;
620 ui
->arranger
->unclone_flag
= 1;
621 ui
->arranger
->split_flag
= 0;
622 ui
->arranger
->join_flag
= 0;
625 ui
->edit_button
->state(0);
626 ui
->color_button
->state(0);
627 ui
->unclone_button
->state(0);
628 ui
->split_button
->state(1);
629 ui
->join_button
->state(0);
630 ui
->arranger
->color_flag
= 0;
631 ui
->arranger
->unclone_flag
= 0;
632 ui
->arranger
->split_flag
= 1;
633 ui
->arranger
->join_flag
= 0;
636 ui
->edit_button
->state(0);
637 ui
->color_button
->state(0);
638 ui
->unclone_button
->state(0);
639 ui
->split_button
->state(0);
640 ui
->join_button
->state(1);
641 ui
->arranger
->color_flag
= 0;
642 ui
->arranger
->unclone_flag
= 0;
643 ui
->arranger
->split_flag
= 0;
644 ui
->arranger
->join_flag
= 1;
651 void set_beats_per_measure(int n
){
652 config
.beats_per_measure
= n
;
653 ui
->metronome
->set_N(n
);
654 ui
->piano_roll
->redraw();
655 ui
->arranger
->redraw();
656 ui
->arranger
->q_tick
= n
*TICKS_PER_BEAT
;
657 ui
->song_timeline
->redraw();
658 ui
->pattern_timeline
->redraw();
661 void set_measures_per_phrase(int n
){
662 config
.measures_per_phrase
= n
;
663 ui
->piano_roll
->redraw();
664 ui
->arranger
->redraw();
665 ui
->song_timeline
->redraw();
666 ui
->pattern_timeline
->redraw();
669 void set_beats_per_minute(int n
){
670 config
.beats_per_minute
= n
;
674 void set_measures_until_record(int n
){
675 config
.measures_until_record
= n
;
678 void set_alwayscopy(int v
){
679 config
.alwayscopy
= v
;
682 void set_autotrackname(int v
){
683 config
.autotrackname
= v
;
686 void set_passthru(int v
){
688 backend_set_passthru(v
);
691 void set_playinsert(int v
){
692 config
.playinsert
= v
;
695 void set_recordonchan(int v
){
696 config
.recordonchan
= v
;
699 void set_playmove(int v
){
703 void set_follow(int v
){
707 void set_recordmode(int n
){
708 config
.recordmode
= n
;
711 void set_robmode(int n
){
715 void set_defaultvelocity(int n
){
716 config
.defaultvelocity
= n
;
719 void set_trackinit(int n
){
720 config
.trackinit
= n
;
721 backend_set_trackinit(n
);
732 fltk::TextBuffer
* ptr
= ui
->scope
->buffer();
733 ptr
->remove(0,ptr
->length());
737 void scope_print(const char* text
){
739 ui
->scope
->append(text
);
740 int N
= ui
->scope
->buffer()->length();
741 ui
->scope
->scroll(N
,0);
747 void show_song_edit(){
748 ui
->pattern_edit
->hide();
749 ui
->pattern_buttons
->hide();
750 ui
->song_edit
->activate();
751 ui
->song_edit
->show();
752 ui
->song_edit
->take_focus();
753 ui
->song_buttons
->show();
756 void show_pattern_edit(){
757 ui
->song_edit
->hide();
758 ui
->song_edit
->deactivate();
759 ui
->song_buttons
->hide();
760 ui
->pattern_edit
->take_focus();
761 ui
->pattern_edit
->show();
762 ui
->pattern_buttons
->show();
767 //switch between normal, note off, portamento, and aftertouch
772 ui
->tool_button
->copy_label("80");
773 ui
->tool_button
->state(1);
777 ui
->tool_button
->copy_label("A0");
781 ui
->tool_button
->copy_label("po");
785 ui
->tool_button
->copy_label("tool");
786 ui
->tool_button
->state(0);
797 for(int i
=0; i
<16; i
++){
805 ui
->track_info
->set_rec(0);
806 ui
->track_info
->update();
807 ui
->action_window
->hide();
812 void add_track(track
* t
){
814 ui
->track_info
->add_track();
817 void remove_track(int n
){
825 ui
->arranger
->layout();
826 ui
->song_vscroll
->slider_size(60);
827 ui
->song_vscroll
->value(0);
829 ui
->pattern_timeline
->edit_flag
= 1;
830 ui
->pattern_timeline
->zoom
= 15;
831 ui
->pattern_vscroll
->minimum(12*75);
832 ui
->pattern_vscroll
->maximum(0);
833 ui
->pattern_vscroll
->value(300);
834 ui
->pattern_vscroll
->slider_size(50);
835 ui
->pattern_hscroll
->value(0);
839 ui
->main_window
->hide();
840 ui
->config_window
->hide();
841 ui
->help_window
->hide();
842 ui
->action_window
->hide();
843 ui
->scope_window
->hide();