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
38 #define CONFIG_FILENAME ".epichordrc"
41 extern std::vector
<track
*> tracks
;
47 char* config_filename
;
54 char* homepath
= getenv("HOME");
55 asprintf(&config_filename
,"%s/"CONFIG_FILENAME
,homepath
);
58 f
.open(config_filename
,fstream::in
);
60 printf("load_config: 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;
67 config
.playinsert
= 1;
68 config
.recordonchan
= 0;
71 config
.recordmode
= 0;
73 config
.defaultvelocity
= 96;
75 load_default_keymap();
80 config
.beats_per_measure
= 4;
81 config
.measures_per_phrase
= 4;
88 if(word
== "leadin"){f
>>config
.measures_until_record
;}
89 else if(word
== "alwayscopy"){f
>>config
.alwayscopy
;}
90 else if(word
== "autotrackname"){f
>>config
.autotrackname
;}
91 else if(word
== "passthru"){f
>>config
.passthru
;}
92 else if(word
== "playinsert"){f
>>config
.playinsert
;}
93 else if(word
== "recordonchan"){f
>>config
.recordonchan
;}
94 else if(word
== "playmove"){f
>>config
.playmove
;}
95 else if(word
== "follow"){f
>>config
.follow
;}
96 else if(word
== "recordmode"){f
>>config
.recordmode
;}
97 else if(word
== "robmode"){f
>>config
.robmode
;}
98 else if(word
== "keymap"){load_keymap(f
);}
99 else if(word
== "defaultvelocity"){f
>>config
.defaultvelocity
;}
101 f
.ignore(std::numeric_limits
<streamsize
>::max(),'\n');
110 f
.open(config_filename
,fstream::out
);
112 printf("save_config: Unable to open config file %s for saving.\n", config_filename
);
116 f
<< "leadin " << config
.measures_until_record
<< endl
;
117 f
<< "alwayscopy " << config
.alwayscopy
<< endl
;
118 f
<< "autotrackname " << config
.autotrackname
<< endl
;
119 f
<< "passthru " << config
.passthru
<< endl
;
120 f
<< "playinsert " << config
.playinsert
<< endl
;
121 f
<< "recordonchan " << config
.recordonchan
<< endl
;
122 f
<< "playmove " << config
.playmove
<< endl
;
123 f
<< "follow " << config
.follow
<< endl
;
124 //f << "quantizedur " << config.quantizedur << endl;
125 f
<< "recordmode " << config
.recordmode
<< endl
;
126 f
<< "robmode " << config
.robmode
<< endl
;
127 f
<< "defaultvelocity " << config
.defaultvelocity
<< endl
;
133 void update_config_gui(){
134 ui
->beats_per_measure
->value(config
.beats_per_measure
);
135 ui
->measures_per_phrase
->value(config
.measures_per_phrase
);
136 ui
->measures_until_record
->value(config
.measures_until_record
);
138 ui
->bpm_wheel
->value(config
.beats_per_minute
);
139 ui
->bpm_output
->value(config
.beats_per_minute
);
141 ui
->check_alwayscopy
->state(config
.alwayscopy
);
142 ui
->check_autotrackname
->state(config
.autotrackname
);
143 ui
->check_passthru
->state(config
.passthru
);
144 ui
->check_playinsert
->state(config
.playinsert
);
145 ui
->check_recordonchan
->state(config
.recordonchan
);
146 ui
->check_playmove
->state(config
.playmove
);
147 ui
->check_follow
->state(config
.follow
);
149 ui
->menu_recordmode
->value(config
.recordmode
);
150 ui
->menu_rob
->value(config
.robmode
);
152 ui
->default_velocity
->value(config
.defaultvelocity
);
154 ui
->config_window
->redraw();
159 seqpat
* rob_check(seqpat
* s
){
160 seqpat
* prev
= s
->prev
;
162 if(config
.robmode
== 0){
165 else if(config
.robmode
== 1 || prev
== NULL
){
166 int pos
= get_play_position();
167 int M
= config
.measures_per_phrase
*config
.beats_per_measure
*128;
171 int R
= s
->tick
+s
->dur
;
177 int L
= s
->next
->tick
;
182 c
= new CreateSeqpatBlank(s
->track
,T
,W
);
187 else if(config
.robmode
== 2){
188 int pos
= get_play_position();
189 int M
= config
.measures_per_phrase
*config
.beats_per_measure
*128;
190 int P
= pos
/M
*M
+ M
;//tick at next phrase boundary
193 int W2
= s
->next
->tick
- s
->tick
;
198 c
= new ResizeSeqpat(s
,W
);
208 void playing_timeout_cb(void* v
){
209 int pos
= get_play_position();
212 reset_record_flags();
217 ui
->arranger
->update(pos
);
218 ui
->piano_roll
->update(pos
);
220 ui
->song_timeline
->update(pos
);
221 ui
->pattern_timeline
->update(pos
);
222 ui
->metronome
->update(pos
);
224 //check for midi input
231 track
* t
= tracks
[get_rec_track()];
238 while(recv_midi(&chan
,&tick
,&type
,&val1
,&val2
)){
240 if(config
.recordonchan
){
241 for(int i
=0; i
<tracks
.size(); i
++){
242 if(tracks
[i
]->chan
== chan
){
250 snprintf(report
,256,"%02x %02x %02x : note off - ch %d note %d vel %d\n",type
|chan
,val1
,val2
,chan
,val1
,val2
);
253 if(!is_backend_recording())
256 s
= tfind
<seqpat
>(t
->head
,tick
);
257 if(s
->tick
+s
->dur
< tick
){
262 //if(s->record_flag==1 && config.recordmode>0){show_song_edit();}
263 s
->record_check(config
.recordmode
);
265 c
=new CreateNoteOff(p
,val1
,val2
,tick
-s
->tick
);
268 if(ui
->piano_roll
->visible()){
269 ui
->piano_roll
->redraw();
270 ui
->event_edit
->redraw();
271 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[1]=1;}
272 ui
->event_menu
->redraw();
274 if(ui
->arranger
->visible())
275 ui
->arranger
->redraw();
278 snprintf(report
,256,"%02x %02x %02x : note on - ch %d note %d vel %d\n",type
|chan
,val1
,val2
,chan
,val1
,val2
);
281 if(!is_backend_recording())
284 s
= tfind
<seqpat
>(t
->head
,tick
);
285 if(s
->tick
+s
->dur
< tick
){
290 // if(s->record_flag==1 && config.recordmode>0){show_song_edit();}
291 s
->record_check(config
.recordmode
);
293 c
=new CreateNoteOn(p
,val1
,val2
,tick
-s
->tick
,16);
296 if(ui
->piano_roll
->visible()){
297 ui
->piano_roll
->redraw();
298 ui
->event_edit
->redraw();
299 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[0]=1;}
300 ui
->event_menu
->redraw();
302 if(ui
->arranger
->visible())
303 ui
->arranger
->redraw();
305 case 0xa0://aftertouch
306 case 0xb0://controller
307 case 0xc0://program change
308 case 0xd0://channel pressure
309 case 0xe0://pitch wheel
311 s
= tfind
<seqpat
>(t
->head
,tick
);
312 if(s
->tick
+s
->dur
< tick
){
319 snprintf(report
,256,"%02x %02x %02x : aftertouch - ch %d note %d %d\n",type
|chan
,val1
,val2
,chan
,val1
,val2
);
320 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[2]=1;}
323 snprintf(report
,256,"%02x %02x %02x : controller change - ch %d cntr %d val %d\n",type
|chan
,val1
,val2
,chan
,val1
,val2
);
324 if(ui
->event_edit
->cur_seqpat
== s
){
325 ui
->event_edit
->has
[val1
+6]=1;
329 snprintf(report
,256,"%02x %02x : program change - ch %d pgrm %d \n",type
|chan
,val1
,chan
,val1
);
330 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[3]=1;}
333 snprintf(report
,256,"%02x %02x : channel pressure - ch %d val %d \n",type
|chan
,val1
,chan
,val1
);
334 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[4]=1;}
337 snprintf(report
,256,"%02x %02x %02x : pitch wheel - ch %d val %d \n",type
|chan
,val1
,val2
,chan
,(val2
<<7)|val1
);
338 if(ui
->event_edit
->cur_seqpat
== s
){ui
->event_edit
->has
[5]=1;}
343 if(!is_backend_recording())
346 // if(s->record_flag==1 && config.recordmode>0){show_song_edit();}
347 s
->record_check(config
.recordmode
);
349 c
=new CreateEvent(p
,type
,tick
,val1
,val2
);
352 if(ui
->piano_roll
->visible()){
353 ui
->piano_roll
->redraw();
354 ui
->event_edit
->redraw();
355 ui
->event_menu
->redraw();
357 if(ui
->arranger
->visible())
358 ui
->arranger
->redraw();
362 case 1://undefined (reserved) system common message
363 snprintf(report
,256,"%02x : undefined (reserved) system common message\n",type
|chan
);
365 case 2://song position pointer
366 snprintf(report
,256,"%02x %02x %02x : song position - %d \n",type
|chan
,val1
,val2
,(val2
<<7)|val1
);
369 snprintf(report
,256,"%02x %02x : song select - %d \n",type
|chan
,val1
,val1
);
371 case 4://undefined (reserved) system common message
372 case 5://undefined (reserved) system common message
373 snprintf(report
,256,"%02x : undefined (reserved) system common message\n",type
|chan
);
375 case 6://tune request
376 snprintf(report
,256,"%02x : tune request\n",type
|chan
);
378 case 7://end of exclusive
379 snprintf(report
,256,"%02x : end of exclusive\n",type
|chan
);
381 case 8://timing clock
382 snprintf(report
,256,"%02x : timing clock\n",type
|chan
);
384 case 9://undefined (reserved) system common message
385 snprintf(report
,256,"%02x : undefined (reserved) system common message\n",type
|chan
);
388 snprintf(report
,256,"%02x : start\n",type
|chan
);
391 snprintf(report
,256,"%02x : continue\n",type
|chan
);
394 snprintf(report
,256,"%02x : stop\n",type
|chan
);
397 snprintf(report
,256,"%02x : undefined (reserved) system common message\n",type
|chan
);
399 case 14://active sensing
400 snprintf(report
,256,"%02x : active sensing\n",type
|chan
);
403 snprintf(report
,256,"%02x : reset\n",type
|chan
);
407 snprintf(report
,256,"%02x %02x : system exclusive - id %d ; data follows\n",type
|chan
,val1
,val1
);
409 scope_print(getsysexbuf());
410 scope_print("\nf7 : end of sysex\n");
420 //handle session events (LASH)
422 char* session_string
;
423 char* filename_string
;
424 ret
=backend_session_process();
425 while(ret
!= SESSION_NOMORE
){
426 session_string
=get_session_string();
427 filename_string
= (char*)malloc(strlen(session_string
)+16);
428 strcpy(filename_string
,session_string
);
429 strcat(filename_string
,"/song.epi");
431 case SESSION_SAVE
: save(filename_string
); break;
432 case SESSION_LOAD
: load(filename_string
); break;
433 case SESSION_QUIT
: ui
->main_window
->hide(); break;
434 case SESSION_UNHANDLED
: break;
436 free(session_string
);
437 ret
=backend_session_process();
441 if(is_backend_playing()){
442 fltk::repeat_timeout(0.005, playing_timeout_cb
, NULL
);
445 fltk::repeat_timeout(0.1, playing_timeout_cb
, NULL
);
449 void start_monitor(){
450 fltk::add_timeout(0.1, playing_timeout_cb
, NULL
);
454 if(!is_backend_playing()){
456 ui
->play_button
->label("@||");
457 //fltk::add_timeout(0.01, playing_timeout_cb, NULL);
462 ui
->play_button
->label("@>");
468 //stops playback and sets the play position to zero
473 ui
->song_timeline
->update(0);
474 ui
->pattern_timeline
->update(0);
476 ui
->song_timeline
->redraw();
477 ui
->pattern_timeline
->redraw();
479 ui
->play_button
->label("@>");
480 ui
->play_button
->redraw();
482 ui
->metronome
->update(0);
487 void set_quant(int q
){
490 ui
->qbutton4
->state(0);
491 ui
->qbutton8
->state(0);
492 ui
->qbutton16
->state(0);
493 ui
->qbutton32
->state(0);
494 ui
->qbutton64
->state(0);
495 ui
->qbutton128
->state(0);
496 ui
->qbutton0
->state(1);
497 ui
->piano_roll
->set_qtick(1);
500 ui
->qbutton4
->state(1);
501 ui
->qbutton8
->state(0);
502 ui
->qbutton16
->state(0);
503 ui
->qbutton32
->state(0);
504 ui
->qbutton64
->state(0);
505 ui
->qbutton128
->state(0);
506 ui
->qbutton0
->state(0);
507 ui
->piano_roll
->set_qtick(128);
510 ui
->qbutton4
->state(0);
511 ui
->qbutton8
->state(1);
512 ui
->qbutton16
->state(0);
513 ui
->qbutton32
->state(0);
514 ui
->qbutton64
->state(0);
515 ui
->qbutton128
->state(0);
516 ui
->qbutton0
->state(0);
517 ui
->piano_roll
->set_qtick(64);
520 ui
->qbutton4
->state(0);
521 ui
->qbutton8
->state(0);
522 ui
->qbutton16
->state(1);
523 ui
->qbutton32
->state(0);
524 ui
->qbutton64
->state(0);
525 ui
->qbutton128
->state(0);
526 ui
->qbutton0
->state(0);
527 ui
->piano_roll
->set_qtick(32);
530 ui
->qbutton4
->state(0);
531 ui
->qbutton8
->state(0);
532 ui
->qbutton16
->state(0);
533 ui
->qbutton32
->state(1);
534 ui
->qbutton64
->state(0);
535 ui
->qbutton128
->state(0);
536 ui
->qbutton0
->state(0);
537 ui
->piano_roll
->set_qtick(16);
540 ui
->qbutton4
->state(0);
541 ui
->qbutton8
->state(0);
542 ui
->qbutton16
->state(0);
543 ui
->qbutton32
->state(0);
544 ui
->qbutton64
->state(1);
545 ui
->qbutton128
->state(0);
546 ui
->qbutton0
->state(0);
547 ui
->piano_roll
->set_qtick(8);
550 ui
->qbutton4
->state(0);
551 ui
->qbutton8
->state(0);
552 ui
->qbutton16
->state(0);
553 ui
->qbutton32
->state(0);
554 ui
->qbutton64
->state(0);
555 ui
->qbutton128
->state(1);
556 ui
->qbutton0
->state(0);
557 ui
->piano_roll
->set_qtick(4);
563 void set_beats_per_measure(int n
){
564 config
.beats_per_measure
= n
;
565 ui
->metronome
->set_N(n
);
566 ui
->piano_roll
->redraw();
567 ui
->arranger
->redraw();
568 ui
->arranger
->q_tick
= n
*TICKS_PER_BEAT
;
569 ui
->song_timeline
->redraw();
570 ui
->pattern_timeline
->redraw();
573 void set_measures_per_phrase(int n
){
574 config
.measures_per_phrase
= n
;
575 ui
->piano_roll
->redraw();
576 ui
->arranger
->redraw();
577 ui
->song_timeline
->redraw();
578 ui
->pattern_timeline
->redraw();
581 void set_beats_per_minute(int n
){
582 config
.beats_per_minute
= n
;
586 void set_measures_until_record(int n
){
587 config
.measures_until_record
= n
;
590 void set_alwayscopy(int v
){
591 config
.alwayscopy
= v
;
594 void set_autotrackname(int v
){
595 config
.autotrackname
= v
;
598 void set_passthru(int v
){
600 backend_set_passthru(v
);
603 void set_playinsert(int v
){
604 config
.playinsert
= v
;
607 void set_recordonchan(int v
){
608 config
.recordonchan
= v
;
611 void set_playmove(int v
){
615 void set_follow(int v
){
619 void set_recordmode(int n
){
620 config
.recordmode
= n
;
623 void set_robmode(int n
){
627 void set_defaultvelocity(int n
){
628 config
.defaultvelocity
= n
;
639 fltk::TextBuffer
* ptr
= ui
->scope
->buffer();
640 ptr
->remove(0,ptr
->length());
644 void scope_print(const char* text
){
646 ui
->scope
->append(text
);
647 int N
= ui
->scope
->buffer()->length();
648 ui
->scope
->scroll(N
,0);
654 void show_song_edit(){
655 ui
->pattern_edit
->hide();
656 ui
->pattern_buttons
->hide();
657 ui
->song_edit
->activate();
658 ui
->song_edit
->show();
659 ui
->song_edit
->take_focus();
660 ui
->song_buttons
->show();
663 void show_pattern_edit(){
664 ui
->song_edit
->hide();
665 ui
->song_edit
->deactivate();
666 ui
->song_buttons
->hide();
667 ui
->pattern_edit
->take_focus();
668 ui
->pattern_edit
->show();
669 ui
->pattern_buttons
->show();
674 //switch between normal, note off, portamento, and aftertouch
679 ui
->tool_button
->copy_label("80");
680 ui
->tool_button
->state(1);
684 ui
->tool_button
->copy_label("A0");
688 ui
->tool_button
->copy_label("po");
692 ui
->tool_button
->copy_label("tool");
693 ui
->tool_button
->state(0);
704 for(int i
=0; i
<16; i
++){
711 ui
->track_info
->update();
712 ui
->action_window
->hide();
717 void add_track(track
* t
){
719 ui
->track_info
->add_track();
722 void remove_track(int n
){