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
30 #include <arpa/inet.h>
34 #include <fltk/file_chooser.h>
35 #include <fltk/filename.h>
51 extern std::vector
<track
*> tracks
;
54 extern struct conf config
;
58 std::string last_filename
;
59 char last_dir
[1024] = "";
62 void nextline(ifstream
& f
){
69 std::list
<pattern
*> collectpatterns(){
70 std::list
<pattern
*> patlist
;
72 for(int i
=0; i
<tracks
.size(); i
++){
73 seqpat
* s
= tracks
[i
]->head
->next
;
76 for(int j
=0; j
<s
->layers
->total
; j
++){
77 pattern
* p
= s
->layers
->array
[j
];
82 patlist
.push_back(s
->p
);
94 int findpatternindex(pattern
* p
, std::list
<pattern
*>& patlist
){
95 std::list
<pattern
*>::iterator i
= patlist
.begin();
97 while(i
!= patlist
.end()){
107 pattern
* findpatternbyindex(int index
, std::list
<pattern
*>& patlist
){
108 std::list
<pattern
*>::iterator i
= patlist
.begin();
110 while(i
!= patlist
.end()){
128 set_beats_per_measure(4);
129 set_measures_per_phrase(4);
132 set_beats_per_minute(120);
136 int total
= tracks
.size();
138 for(int i
=0; i
<total
; i
++){
139 t
= tracks
[tracks
.size()-1];
147 ui
->title_text
->text("");
148 ui
->author_text
->text("");
150 fltk::TextBuffer
* tb
= ui
->info_text
->buffer();
153 ui
->track_info
->clear_tracks();
155 ui
->main_window
->redraw();
159 void set_last_dir(const char* name
){
160 const char* theend
= fltk::filename_name(name
);
161 int n
= (int)(theend
- name
) / sizeof(char);
162 memcpy(last_dir
,name
,n
);
166 const char* get_last_dir(){
171 if(last_filename
!= ""){
172 return save(last_filename
.c_str());
175 return save(fltk::file_chooser("save file",NULL
,last_dir
));
181 int save(const char* filename
){
183 if(filename
== NULL
){
189 file
.open(filename
, fstream::out
);
192 printf("error, cant open file for saving\n");
196 last_filename
= filename
;
197 set_last_dir(filename
);
199 //header to protect against accidentally opening wrong file
200 file
<< "J2ULJwCgwHA" << endl
;
201 file
<< "epichord" << endl
;
202 file
<< "fileversion " << FILE_VERSION
<< endl
;
203 file
<< "ticksperbeat " << TICKS_PER_BEAT
<< endl
<< endl
;
206 file
<< "title " << ui
->title_text
->size() << " "
207 << ui
->title_text
->text() << endl
;
208 file
<< "author " << ui
->author_text
->size() << " "
209 << ui
->author_text
->text() << endl
;
210 file
<< "info " << ui
->info_text
->size() << " "
211 << ui
->info_text
->text() << endl
;
213 file
<< "bpm " << ui
->bpm_wheel
->value() << endl
;
214 file
<< "beatspermeasure " << config
.beats_per_measure
<< endl
;
215 file
<< "measuresperphrase " << config
.measures_per_phrase
<< endl
;
217 file
<< "loopstart " << get_loop_start() << endl
;
218 file
<< "loopend " << get_loop_end() << endl
;
223 //collect all visible patterns
224 std::list
<pattern
*> patlist
= collectpatterns();
227 std::list
<pattern
*>::iterator p
= patlist
.begin();
230 while(p
!= patlist
.end()){
231 file
<< "pattern " << endl
;
232 file
<< (*p
)->h
<< " " << (*p
)->s
<< " " << (*p
)->v
<< endl
;
233 e
= (*p
)->events
->next
;
240 delta
= e
->tick
- last_tick
;
242 file
<< delta
<< " ";
243 file
<< e
->type
<< " ";
244 file
<< e
->value1
<< " ";
245 file
<< e
->value2
<< endl
;
246 //file << e->dur << endl;
251 file
<< -1 << endl
<< endl
;
256 for(int i
=0; i
<tracks
.size(); i
++){
257 file
<< endl
<< endl
<< "track " << endl
;
258 file
<< "port " << tracks
[i
]->port
<< endl
;
259 file
<< "chan " << tracks
[i
]->chan
<< endl
;
260 file
<< "prog " << tracks
[i
]->prog
<< endl
;
261 file
<< "bank " << tracks
[i
]->bank
<< endl
;
262 file
<< "mute " << tracks
[i
]->mute
<< endl
;
263 file
<< "solo " << tracks
[i
]->solo
<< endl
;
264 file
<< "vol " << tracks
[i
]->vol
<< endl
;
265 file
<< "pan " << tracks
[i
]->pan
<< endl
;
266 file
<< "name " << strlen(tracks
[i
]->name
) << " "
267 << tracks
[i
]->name
<< endl
;
268 file
<< "alive " << tracks
[i
]->alive
<< endl
;
269 s
= tracks
[i
]->head
->next
;
272 file
<< endl
<< endl
<< "seqpat " << endl
;
273 file
<< s
->tick
<< " " << s
->dur
<< endl
;
274 file
<< s
->scrollx
<< " " << s
->scrolly
<< endl
;
277 file
<< s
->layers
->total
<< " ";
278 for(int j
=0; j
<s
->layers
->total
; j
++){
280 int index
= findpatternindex(s
->layers
->array
[j
],patlist
);
282 printf("save: pattern not found, cannot save\n");
286 file
<< index
<< " ";
289 file
<< s
->layers
->index
;
293 int index
= findpatternindex(s
->p
,patlist
);
295 printf("save: pattern not found, cannot save\n");
319 int load(const char* filename
){
322 if(filename
== NULL
){
328 file
.open(filename
, fstream::in
);
331 printf("error, cant open file for saving\n");
338 last_filename
= filename
;
339 set_last_dir(filename
);
341 //pattern* pend = patterns;
342 std::list
<pattern
*> patlist
;
347 if(str
!= "J2ULJwCgwHA"){
348 printf("load: this is definitely not a valid file (missing magic)\n");
353 if(str
!= "epichord"){
354 printf("load: this is definitely not a valid file (missing magic)\n");
359 if(str
!= "fileversion"){
360 printf("load: this is definitely not a valid file\n");
367 printf("load: file has wrong version %d (need %d).\n",M
,FILE_VERSION
);
370 if(str
!= "ticksperbeat"){
371 printf("load: file is broken. (missing ticks per beat)\n");
378 printf("load: file is broken. (bad ticks per beat %d)\n",file_tpb
);
395 ui
->title_text
->text(buf
);
397 else if(str
== "author"){
403 ui
->author_text
->text(buf
);
405 else if(str
== "info"){
408 fltk::TextBuffer
* tb
= ui
->info_text
->buffer();
414 else if(str
== "bpm"){
418 ui
->bpm_wheel
->value(bpm
);
419 ui
->bpm_output
->value(bpm
);
421 else if(str
== "beatspermeasure"){
424 set_beats_per_measure(N
);
426 else if(str
== "measuresperphrase"){
429 set_measures_per_phrase(N
);
431 else if(str
== "loopstart"){
436 else if(str
== "loopend"){
441 else if(str
== "pattern"){
442 pattern
* p
= new pattern();
447 mevent
* eend
= p
->events
;
457 if(delta
== -1){delete e
; break;}
470 patlist
.push_back(p
);
472 else if(str
== "track"){
475 track
* t
= new track();
476 t
->head
->track
= tracks
.size();
477 seqpat
* send
= t
->head
;
480 while(key
!= "kcart"){
481 if(key
== "port"){ file
>> t
->port
; }
482 else if(key
== "chan"){ file
>> t
->chan
; }
483 else if(key
== "prog"){ file
>> t
->prog
; }
484 else if(key
== "bank"){ file
>> t
->bank
; }
485 else if(key
== "mute"){ file
>> t
->mute
; }
486 else if(key
== "solo"){ file
>> t
->solo
; }
487 else if(key
== "vol"){ file
>> t
->vol
; }
488 else if(key
== "pan"){ file
>> t
->pan
; }
489 else if(key
== "name"){
492 char* buf
= (char*)malloc(n
+1);
497 else if(key
== "alive"){ file
>> t
->alive
; }
498 else if(key
== "seqpat"){
500 seqpat
* s
= new seqpat();
501 s
->track
= tracks
.size();
508 file
>> s
->scrollx
>> s
->scrolly
;
509 file
>> total_layers
;
511 if(total_layers
== 1){
513 p
= findpatternbyindex(index
, patlist
);
517 else if(total_layers
> 1){
519 p
= findpatternbyindex(index
, patlist
);
520 layerstack
* layers
= new layerstack(p
);
522 for(int j
=1; j
<total_layers
; j
++){
524 p
= findpatternbyindex(index
,patlist
);
528 file
>> layers
->index
;
529 s
->p
= layers
->array
[layers
->index
];
534 printf("load: bad number of layers\n");
546 ui
->track_info
->add_track();
549 file
.ignore(std::numeric_limits
<streamsize
>::max(),'\n');
556 ui
->track_info
->set_rec(0);
557 ui
->track_info
->update();
559 ui
->arranger
->redraw();
571 int tick2delta(unsigned tick
, vector
<unsigned char>& vbuf
){
572 unsigned char buf
[4];
575 vbuf
.push_back(tick
);
578 else if(tick
< 16383){
581 vbuf
.push_back(0x80|((buf
[0]<<1)|(buf
[1]&0x80)>>7));
582 vbuf
.push_back(buf
[1]&0x7f);
585 else if(tick
< 2097151){
588 vbuf
.push_back(0x80|((buf
[0]<<2)|(buf
[1]&0xC0)>>6));
589 vbuf
.push_back(0x80|((buf
[1]<<1)|(buf
[2]&0x80)>>7));
590 vbuf
.push_back(buf
[2]&0x7f);
596 vbuf
.push_back(0x80|((buf
[0]<<3)|(buf
[1]&0xE0)>>5));
597 vbuf
.push_back(0x80|((buf
[1]<<2)|(buf
[2]&0xC0)>>6));
598 vbuf
.push_back(0x80|((buf
[2]<<1)|(buf
[3]&0x80)>>7));
599 vbuf
.push_back(buf
[3]&0x7f);
604 int savesmf(const char* filename
){
606 if(filename
== NULL
){
611 file
.open(filename
, fstream::out
);
614 printf("error, cant open file for saving\n");
618 unsigned char buf
[64];
637 uint16_t L
= tracks
.size();
641 //128 ticks per beat, 128 | 0x0000
645 file
.write((const char*)buf
,14);
647 vector
<unsigned char> vbuf
;
650 int MAX
= tracks
.size();
652 for(int i
=0; i
<MAX
; i
++){
663 //chunk size (yet unknown)
665 vbuf
.push_back(0x00);
666 vbuf
.push_back(0x00);
667 vbuf
.push_back(0x00);
668 vbuf
.push_back(0x00);
670 track
* t
= tracks
[i
];
675 //change program for this track
681 e
= s
->p
->events
->next
;
685 if(e
->tick
> s
->dur
){
690 N
+= tick2delta(e
->tick
+s
->tick
- last_tick
,vbuf
);
691 last_tick
= e
->tick
+s
->tick
;
692 vbuf
.push_back(0x80 | t
->chan
);
693 vbuf
.push_back(e
->value1
);
694 vbuf
.push_back(e
->value2
);
698 N
+= tick2delta(e
->tick
+s
->tick
- last_tick
,vbuf
);
699 last_tick
= e
->tick
+s
->tick
;
700 vbuf
.push_back(0x90 | t
->chan
);
701 vbuf
.push_back(e
->value1
);
702 vbuf
.push_back(e
->value2
);
705 case 0xA0://after touch
706 N
+= tick2delta(e
->tick
+s
->tick
- last_tick
,vbuf
);
707 last_tick
= e
->tick
+s
->tick
;
708 vbuf
.push_back(0xA0 | t
->chan
);
709 vbuf
.push_back(e
->value1
);
710 vbuf
.push_back(e
->value2
);
713 case 0xB0://control change
714 N
+= tick2delta(e
->tick
+s
->tick
- last_tick
,vbuf
);
715 last_tick
= e
->tick
+s
->tick
;
716 vbuf
.push_back(0xB0 | t
->chan
);
717 vbuf
.push_back(e
->value1
);
718 vbuf
.push_back(e
->value2
);
721 case 0xC0://program change
722 N
+= tick2delta(e
->tick
+s
->tick
- last_tick
,vbuf
);
723 last_tick
= e
->tick
+s
->tick
;
724 vbuf
.push_back(0xC0 | t
->chan
);
725 vbuf
.push_back(e
->value1
);
728 case 0xD0://channel pressure
729 N
+= tick2delta(e
->tick
+s
->tick
- last_tick
,vbuf
);
730 last_tick
= e
->tick
+s
->tick
;
731 vbuf
.push_back(0xD0 | t
->chan
);
732 vbuf
.push_back(e
->value1
);
735 case 0xE0://pitch wheel
736 N
+= tick2delta(e
->tick
+s
->tick
- last_tick
,vbuf
);
737 last_tick
= e
->tick
+s
->tick
;
738 vbuf
.push_back(0xE0 | t
->chan
);
739 vbuf
.push_back(e
->value1
);
740 vbuf
.push_back(e
->value2
);
750 //end of track meta event
751 vbuf
.push_back(0x00);
752 vbuf
.push_back(0xff);
753 vbuf
.push_back(0x2f);
754 vbuf
.push_back(0x00);
764 for(int i
=0; i
<vbuf
.size(); i
++){
766 file
.write((const char*)buf
,1);
776 int getdelta(std::fstream
& f
){
777 unsigned char a
,b
,c
,d
;
780 if(a
<0x80){return a
;}
783 if(b
<0x80){return ((a
&0x7f)<<7) | b
;}
786 if(c
<0x80){return ((a
&0x7f)<<14) | ((b
&0x7f)<<7) | c
;}
789 return ((a
&0x7f)<<21) | ((b
&0x7f)<<14) | ((c
&0x7f)<<7) | d
;
797 char fifths
[15][8] = {
816 int loadsmf(const char* filename
){
817 if(filename
== NULL
){
822 file
.open(filename
, fstream::in
);
825 printf("error, cant open file for saving\n");
829 last_filename
= filename
;
830 set_last_dir(filename
);
834 unsigned char buf
[64];
843 std::list
<pattern
*> patlist
;
844 std::vector
<int> chanlist
;
845 std::vector
<int> proglist
;
846 std::vector
<int> banklist
;
848 std::vector
<track
*> extratracks
;
850 std::vector
<char*> tracknames
;
854 for(int i
=0; i
<16; i
++){
855 extratracks
.push_back(NULL
);
860 file
.read((char*)buf
,4);
861 if(memcmp(buf
,"MThd",4)){
862 printf("missing header, probably not a midi file\n");
867 scope_print("Standard Midi File\n");
869 file
.read((char*)buf
,4);
871 printf("header has wrong size, probably a broken midi file\n");
872 scope_print("error: bad header size");
876 //printf("header size\n");
878 file
.read((char*)buf
,2);
880 printf("bad smf type code, probably a broken midi file\n");
881 scope_print("error: bad smf type");
889 case 0: smftype
=0; break;
890 case 1: smftype
=1; break;
891 case 2: smftype
=2; break;
893 printf("bad smf type code, probably a broken midi file\n");
894 scope_print("error: bad smf type");
899 snprintf(sbuf
,256," type: %d\n",smftype
);
902 file
.read((char*)buf
,2);
903 size
= ntohs(*(unsigned short*)buf
);
905 printf("track count of zero, probably a broken midi file\n");
906 scope_print("error: zero tracks");
912 snprintf(sbuf
,256," tracks: %d\n",ntracks
);
916 int tpb
= TICKS_PER_BEAT
;
917 file
.read((char*)buf
,2);
918 size
= ntohs(*(unsigned short*)buf
);
921 snprintf(sbuf
,256," time division: %d ticks per beat\n",tpb
);
925 int fps
= size
&0x7fff;
926 snprintf(sbuf
,256," time division: %d frames per second (wrong)\n",fps
);
928 scope_print("error: smpte time division not support\n");
935 /*** read tracks ***/
936 file
.read((char*)buf
,4);
939 if(memcmp(buf
,"MTrk",4)){
940 printf("bad track header, probably a broken midi file\n");
945 pattern
* p
= new pattern();
949 snprintf(sbuf
,256," track %d\n",trackindex
);
952 file
.read((char*)buf
,4);
953 size
= ntohl(*(unsigned long*)buf
);
955 printf("empty track\n");
964 chanlist
.push_back(-1);
965 banklist
.push_back(-1);
966 proglist
.push_back(-1);
968 tracknames
.push_back(NULL
);
973 int delta
= getdelta(file
);
975 printf("bad delta time, broken midi file\n");
980 tick
= time
*TICKS_PER_BEAT
/tpb
;
982 if(tick
> maxblockdur
){
987 file
.read((char*)buf
,1);
991 if(byte0
< 0x80){//implicit byte0
997 if(byte0
< 0xf0){//channel event
999 int type
= byte0
&0xf0;
1000 int chan
= byte0
&0x0f;
1002 if(chanlist
[N
]==-1){
1006 if(byte1
<0){//didnt read byte1 yet
1007 file
.read((char*)buf
,1);
1016 if(!(type
== 0xC0 || type
== 0xD0)){
1017 file
.read((char*)buf
,1);
1021 e
= new mevent(type
,tick
,val1
);
1026 if(val2
==0){//fake note off
1031 case 0x80://note off
1032 case 0xA0://aftertouch
1033 case 0xB0://controller change
1034 case 0xE0://pitchbend
1035 if(type
==0xB0 && val1
==0x00 && banklist
[N
]==-1){
1041 case 0xC0://program change
1042 if(proglist
[N
]==-1){
1046 case 0xD0://channel pressure
1049 printf("unrecognized channel event %d\n",type
);
1054 if(chan
!=chanlist
[N
]){//put event in the a misfit track
1055 //printf("mistfit N=%d chan=%d type=%d\n",chanlist[N],chan,type);
1056 if(extratracks
[chan
] == NULL
){
1057 track
* t
= new track();
1063 extratracks
[chan
] = t
;
1064 seqpat
* s
= new seqpat();
1067 s
->p
= new pattern();
1070 extratracks
[chan
]->head
->next
->p
->insert(e
,tick
);
1072 else{//put it in a normal track
1077 else{/*** not a channel event ***/
1081 if(byte0
== 255){//meta events
1083 file
.read((char*)buf
,1);
1087 size
= getdelta(file
);
1089 printf("bad delta time\n");
1095 abuf
= new char[size
+1];
1098 case 0://sequence number
1100 snprintf(sbuf
,256," %d sequence: ? ?\n",time
);
1103 printf("bad sequence number data length: %d\n",size
);
1107 file
.read((char*)buf
,2);
1110 file
.read(abuf
,size
);
1112 asprintf(&tbuf
," %d text: \"%s\"\n",time
,abuf
);
1116 ui
->info_text
->buffer()->append(abuf
);
1117 ui
->info_text
->buffer()->append("\n");
1120 case 2://copyright notice
1121 file
.read((char*)abuf
,size
);
1123 asprintf(&tbuf
," %d copyright: \"%s\"\n",time
,abuf
);
1128 file
.read((char*)abuf
,size
);
1130 asprintf(&tbuf
," %d track name: \"%s\"\n",time
,abuf
);
1134 tracknames
[N
] = (char*)malloc(sizeof(char)*(size
+1));
1135 strncpy(tracknames
[N
],abuf
,size
+1);
1138 case 4://instrument name
1139 file
.read((char*)abuf
,size
);
1141 asprintf(&tbuf
," %d instrument name: \"%s\"\n",time
,abuf
);
1146 file
.read((char*)abuf
,size
);
1148 asprintf(&tbuf
," %d lyrics: \"%s\"\n",time
,abuf
);
1153 file
.read((char*)abuf
,size
);
1155 asprintf(&tbuf
," %d marker: \"%s\"\n",time
,abuf
);
1160 file
.read((char*)abuf
,size
);
1162 asprintf(&tbuf
," %d cue point: \"%s\"\n",time
,abuf
);
1166 case 32://channel prefix
1168 printf("bad channel prefix data size: %d\n",size
);
1172 file
.read((char*)buf
,1);
1173 asprintf(&tbuf
," %d channel prefix: %d\n",time
,buf
[0]);
1177 case 47://end of track
1179 printf("end of track has non zero data size: %d\n",size
);
1187 printf("set tempo has non-3 data size: %d\n",size
);
1191 file
.read((char*)buf
,3);
1198 micros
= *(unsigned*)buf
;
1199 micros
= ntohl(micros
);
1201 asprintf(&tbuf
," %d set tempo: %d us/quarter\n",time
,micros
);
1207 set_beats_per_minute(60000000/micros
);
1211 case 84://smpte offset
1213 printf("smpte offset has non-5 data size: %d\n",size
);
1217 file
.read((char*)buf
,5);
1218 asprintf(&tbuf
," %d smpte offset: ?\n",time
);
1222 case 88://time signature
1224 printf("time signature has non-4 data size: %d\n",size
);
1228 file
.read((char*)buf
,4);
1229 asprintf(&tbuf
," %d time signature: %d/%d\n",time
,buf
[0],1<<buf
[1]);
1233 case 89://key signature
1235 printf("key signature has non-2 data size: %d\n",size
);
1239 file
.read((char*)buf
,2);
1240 asprintf(&tbuf
," %d key signature: %s %s\n",time
,fifths
[(signed char)buf
[0]+7],buf
[1]?"minor":"major");
1244 case 127://sequencer specific
1245 file
.read((char*)abuf
,size
);
1247 asprintf(&tbuf
," %d sequencer specific: \"%s\"\n",time
,abuf
);
1251 default://unknown meta event
1252 file
.read((char*)abuf
,size
);
1254 asprintf(&tbuf
," %d meta %d: \"%s\"\n",time
,meta
,abuf
);
1260 else if(byte0
== 240){//sysex event
1261 size
= getdelta(file
);
1263 printf("bad delta time\n");
1266 abuf
= new char[size
+1];
1267 file
.read((char*)abuf
,size
);
1270 asprintf(&tbuf
," %d sysex: \"%s\"\n",time
,abuf
);
1274 file
.read((char*)buf
,1);
1276 file
.putback(buf
[0]);
1279 scope_print(" end of sysex\n");
1286 else if(byte0
== 247){//divided sysex event
1287 size
= getdelta(file
);
1289 printf("bad delta time\n");
1292 abuf
= new char[size
+1];
1293 file
.read((char*)abuf
,size
);
1296 asprintf(&tbuf
," %d sysex fragment: \"%s\"\n",time
,abuf
);
1300 file
.read((char*)buf
,1);
1302 file
.putback(buf
[0]);
1305 scope_print(" end of sysex\n");
1310 printf("bad event type %d, broken midi file\n",byte0
);
1319 if(proglist
[N
]==-1){
1322 if(banklist
[N
]==-1){
1325 if(chanlist
[N
]==-1){
1333 patlist
.push_back(p
);
1335 file
.read((char*)buf
,4);//read first byte of next track or EOF
1340 scope_print("End Of File\n\n");
1343 //TODO set up track settings using data remembered from the reading
1344 std::list
<pattern
*>::iterator p
= patlist
.begin();
1346 while(p
!=patlist
.end()){
1347 track
* t
= new track();
1348 seqpat
* s
= new seqpat(i
,0,128*64,*p
);
1349 int Q
= TICKS_PER_BEAT
;
1350 s
->dur
= maxblockdur
/ Q
* Q
+ Q
;
1355 s
->p
->h
= 360*i
/ 16;
1357 while(s
->p
->h
> 360){s
->p
->h
-= 360;}
1358 s
->p
->regen_colors();
1360 t
->chan
= chanlist
[i
];
1361 t
->prog
= proglist
[i
];
1362 t
->bank
= banklist
[i
];
1366 t
->name
= tracknames
[i
];
1369 t
->name
= (char*)malloc(8);
1378 if(tempo_flag
== 0){
1379 set_beats_per_minute(120);
1383 ui
->track_info
->set_rec(0);
1384 ui
->track_info
->update();
1386 ui
->arranger
->redraw();
1388 update_config_gui();