]> lisas.de Git - terminatorX.git/blob - src/tX_gui.c
Uploaded Version 3.2 into ther repository... Alex
[terminatorX.git] / src / tX_gui.c
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999  Alexander K├Ěnig
4  
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9  
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.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  
19     File: tX_gui.c
20  
21     Description: Contains code for
22                  - terminatorX main window.
23                  - the file dialogs
24                  - the "pre-listen" feature
25                  - and the position update schedule code
26
27     Changes:
28     
29     21 Jul 1999: Added the necessary GUI stuff for the Lowpass Filter
30 */    
31
32 #include <stdio.h>
33 #include <time.h>
34 #include "gtk/gtk.h"
35 #include "tX_types.h"
36 #include "tX_global.h"
37 #include "tX_widget.h"
38 #include "tX_gui.h"
39 #include "tX_wavfunc.h"
40 #include <malloc.h>
41 #include <string.h>
42 #include "turntable.h"
43 #include "tX_engine.h"
44 #include "tX_dialog.h"
45 #include <X11/Xlib.h>
46 #include <gdk/gdkx.h>
47 #ifndef WIN32
48 #include <unistd.h>
49 #endif
50 #include <math.h>
51 #include <pthread.h>
52 #include "wav_file.h"
53 #include "endian.h"
54 #include "version.h"
55
56 #ifdef __cplusplus
57 extern "C" {
58 #endif /* __cplusplus */
59 int disk_status=DISK_IDLE;
60
61 #define set_disk_status(i); disk_status=i;
62
63 /* Here come 'em widgets */
64
65 Window xwindow=(Window) 0;
66 GtkWidget *window;
67 GtkWidget *scratch_btn;
68 GtkWidget *mix_toggle_btn;
69 GtkWidget *mix_slider;
70 GtkWidget *loop_btn;
71 //GtkWidget *xinput_combo;
72 GtkWidget *wav_display;
73 GtkWidget *wav_progress=NULL;
74 GtkWidget *pos_time;
75
76 GSList    *mode_radios=NULL;
77 GtkWidget *mode_normal;
78 GtkWidget *mode_record;
79 GtkWidget *mode_playback;
80
81 GtkWidget *action_btn;
82
83 GtkWidget *options_btn;
84 GtkWidget *about_btn;
85
86 GtkWidget *save_fast;
87 GtkWidget *save_as;
88 GtkWidget *save_fast_mix;
89 GtkWidget *save_as_mix;
90 GtkTooltips *tooltips;
91
92 GtkWidget *lowpass_toggle_btn;
93 GtkWidget *lowpass_slider;
94
95 GtkAdjustment *lowpass_freq;
96 GtkAdjustment *mix_adjustment;
97
98 gint idle_tag=0;
99
100 int engine_busy=0;
101
102 int scratch_busy=0;
103 GdkWindow *scratch_win=NULL;
104 int loop_busy=0;
105 GdkWindow *loop_win=NULL;
106
107 GdkWindow *save_win=NULL;
108 int save_busy=0;
109
110 GdkWindow *save_mix_win=NULL;
111 int save_mix_busy=0;
112
113 char last_scratch[PATH_MAX]="";
114 char last_loop[PATH_MAX]="";
115
116 int display_scratch=0;
117 //GList *xinput_list=NULL;
118
119 pthread_t prelis_thread=0;
120 pthread_mutex_t prelis_play=PTHREAD_MUTEX_INITIALIZER;
121 pthread_mutex_t prelis_stop=PTHREAD_MUTEX_INITIALIZER;
122
123 #define WID_DYN TRUE, TRUE, 0
124 #define WID_FIX FALSE, FALSE, 0
125
126 int posctr=0;
127
128 int db_save_mix=1;
129 int db_save_raw=0;
130 int prelis_host_order=0;
131
132 FILE* prelis_file=NULL;
133
134 void *prelis_run(void *ptr)
135 {
136         char buffer[4096];
137         int bytes=1;
138
139         pthread_mutex_lock(&prelis_play);
140                 
141         vtt_open_dev(vttgl, 0);
142                         
143         while ((pthread_mutex_trylock(&prelis_stop)) && (bytes))
144         {
145                 bytes=fread(&buffer, 1, 2048, prelis_file);
146 #ifdef BIG_ENDIAN_MACHINE
147                 if (prelis_host_order) swapbuffer((int16_t *) buffer, 1024);
148 #endif
149                 if (bytes) write(vttgl->devicefd, buffer, bytes);
150         }
151                         
152         vtt_close_dev(vttgl);
153         
154         if (!bytes) pthread_mutex_lock(&prelis_stop);   
155                 
156         pthread_mutex_unlock(&prelis_stop);
157         pthread_mutex_unlock(&prelis_play);
158         
159         pthread_exit(NULL);
160 }
161
162 void prelis_halt()
163 {
164         void *dummy;
165                 
166         pthread_mutex_unlock(&prelis_stop);
167         pthread_join(prelis_thread, &dummy);
168         
169         wav_close(prelis_file);
170         
171         prelis_thread=0;
172         prelis_file=NULL;
173 }
174
175 void prelis_start(char *name)
176 {
177         wav_sig wav_in;
178         
179         if (!globals.prelis) return;
180         
181         if (prelis_thread) prelis_halt();
182         
183         if (init_wav_read(name, &wav_in))
184 #ifndef USE_SOX_INPUT   
185         if ((wav_in.depth==16) && (wav_in.chans==1))
186 #endif  
187         {
188 #ifdef USE_SOX_INPUT
189         if (wav_in.has_host_order) prelis_host_order=1;
190         else prelis_host_order=0;
191 #endif  
192                 pthread_mutex_lock(&prelis_play);
193                 pthread_mutex_lock(&prelis_stop);
194                 if (!pthread_create(&prelis_thread, NULL, prelis_run, NULL))
195                 {
196                         prelis_file=wav_in.handle;
197                         pthread_mutex_unlock(&prelis_play);                     
198                 }
199                 else
200                 {
201                         pthread_mutex_unlock(&prelis_play);
202                         prelis_thread=0;
203                         wav_close(wav_in.handle);
204                 }
205                 
206         }
207         else wav_close(wav_in.handle);
208 }
209
210 void display_engine_error(int err);
211
212 int time_ctr=0;
213
214 int check_busy()
215 {
216         if (engine_busy)
217         {
218                 tx_note("You have to stop playback first.");
219                 return(1);
220         }
221         return(0);
222 }
223
224 int idle_ctr=0;
225
226 gint pos_update(gpointer data)
227 {
228         int ret;
229         char buffer[40];
230         unsigned long int minu,sec,hun;
231         int pos;        
232         
233         ret=get_engine_status();
234         
235 //      usleep(globals.update_idle); // usleep and pthreads ... strange strange
236
237         if (!ret)
238         {
239                 pos=vttgl->realpos;
240                 gtk_tx_update_pos_display(GTK_TX(wav_display), pos, vttgl->mute_scratch);                       
241                                 
242                 if (globals.time_enable)
243                 {
244                         time_ctr++;     
245                 
246                         if (time_ctr>globals.time_update)
247                         {
248                                 time_ctr=0;
249                                 if (pos>0)
250                                 {
251                                         minu=pos/2646000;
252                                         pos-=2646000*minu;
253         
254                                         sec=pos/44100;
255                                         pos-=44100*sec;
256         
257                                         hun=pos/441;
258                                 }
259                                 else
260                                 {
261                                         minu=sec=hun=0;
262                                 }
263         
264                                 sprintf(buffer, "%02li:%02li.%02li", minu, sec, hun);
265                                 gtk_label_set_text(GTK_LABEL(GTK_BUTTON(pos_time)->child), buffer);
266                                 gdk_flush();
267                         }
268                 }
269                 return (TRUE);
270         }
271         else if(ret==ENG_INIT)
272         {
273                 idle_ctr++;
274                 if (idle_ctr>1000)
275                 {
276                         stop_engine();
277                         tx_note("Error: Position update idle for 1000 cycles. Giving up!\nMaybe you should increase the \"update idle\" value in the option menu.");
278                         engine_busy=0;
279                         gtk_tx_cleanup_pos_display(GTK_TX(wav_display));
280                         time_ctr=0;                     
281                         return (FALSE);
282                 }
283                 return(TRUE);
284         }
285         else
286         {
287                 stop_engine();
288                 engine_busy=0;
289         
290                 if (ret>3)
291                 {
292                         display_engine_error(ret);                      
293                 }
294                 
295                 gtk_tx_cleanup_pos_display(GTK_TX(wav_display));
296                 time_ctr=0;
297                 return(FALSE);
298         }       
299 }
300
301 void lw_msg(int err, char *buffer)
302 {
303         switch (err)
304         {
305 #ifdef USE_SOX_INPUT    
306                 case 1: strcat (buffer, "Wave-File is not mono."); break;
307                 case 2: strcat (buffer, "Wave-File is not 16 Bit."); break;
308                 case 3: strcat (buffer, "Wave-File could not be accessed."); break;
309 #else
310                 case 1: strcat (buffer, "Couldn't open audio file."); break;
311                 case 2: strcat (buffer, "Couldn't open audio file."); break;
312                 case 3: strcat (buffer, "Couldn't open audio file."); break;
313 #endif          
314                 case 4: strcat (buffer, "Could not allocate memory."); break;
315                 case 5: strcat (buffer, "Error while reading data. File corrupt?"); break;
316                 default: strcat (buffer, "Internal Error?");
317         }
318 }
319
320 void set_mix(int mix)
321 {
322         if (mix)
323         {
324                 if (globals.loop_data)
325                 {
326                         globals.do_mix=1;
327                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mix_toggle_btn), 1);
328                 }
329                 else
330                 {
331                         globals.do_mix=0;
332                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mix_toggle_btn), 0);
333         
334                         tx_note("Can not enable mix: no loop data available."); 
335                 }
336         }
337         else
338         {
339                 globals.do_mix=0;
340                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mix_toggle_btn), 0);
341         }
342 }
343
344 void load_scratch(GtkWidget *widget, GtkFileSelection *fs)
345 {
346         int ret;
347         char newfile[PATH_MAX];
348         char buffer[1024]="Couldn't open scratch file: ";
349         char *fn;
350
351         prelis_halt();
352
353         if (check_busy()) return;
354         
355         set_disk_status(DISK_READING);
356         
357         strcpy(newfile, gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
358         strcpy(last_scratch, newfile);
359         
360         gtk_widget_destroy(GTK_WIDGET(fs));
361
362         if (globals.scratch_data) free(globals.scratch_data);
363
364         ret = load_wav(newfile, &globals.scratch_data, &globals.scratch_size);
365         
366         if (ret)
367         {
368                 strcpy(globals.scratch_name, "");
369                 globals.scratch_data=NULL;
370                 globals.scratch_size=0;
371                 globals.scratch_len=0;
372                 gtk_label_set(GTK_LABEL(GTK_BUTTON(scratch_btn)->child), "< NONE >");
373                 
374                 lw_msg(ret, buffer);
375                 tx_note(buffer);                                
376         }
377         else
378         {
379                 strcpy(globals.scratch_name, newfile);
380                 globals.scratch_len=globals.scratch_size/sizeof(int16_t);
381                 
382                 fn=strrchr(newfile, '/');
383                 if (fn) fn++;
384                 else fn=newfile;
385                 gtk_label_set(GTK_LABEL(GTK_BUTTON(scratch_btn)->child), fn);
386         }       
387         
388         if (!display_scratch)
389         {
390                 gtk_tx_set_data(GTK_TX(wav_display), globals.scratch_data, globals.scratch_len);
391         }
392         
393         scratch_busy=0;
394         scratch_win=NULL;
395         
396         set_disk_status(DISK_IDLE);
397 }
398
399 void load_loop(GtkWidget *widget, GtkFileSelection *fs)
400 {
401         int ret;
402         char newfile[PATH_MAX];
403         char buffer[1024]="Couldn't open loop file: ";
404         char *fn;
405
406         prelis_halt();
407
408         if (check_busy()) return;
409         
410         set_disk_status(DISK_READING);
411         
412         strcpy(newfile, gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
413         strcpy(last_loop, newfile);
414         gtk_widget_destroy(GTK_WIDGET(fs));
415
416         if (globals.loop_data) free(globals.loop_data);
417
418         ret = load_wav(newfile, &globals.loop_data, &globals.loop_size);
419         
420         if (ret)
421         {
422                 strcpy(globals.loop_name, "");
423                 globals.loop_data=NULL;
424                 globals.loop_size=0;
425                 globals.loop_len=0;
426                 gtk_label_set(GTK_LABEL(GTK_BUTTON(loop_btn)->child), "< NONE >");
427
428                 set_mix(0);
429                 
430                 lw_msg(ret, buffer);
431                 tx_note(buffer);                                
432         }
433         else
434         {
435                 strcpy(globals.loop_name, newfile);
436                 globals.loop_len=globals.loop_size/sizeof(int16_t);
437
438                 fn=strrchr(newfile, '/');
439                 if (fn) fn++;
440                 else fn=newfile;
441                 
442                 gtk_label_set(GTK_LABEL(GTK_BUTTON(loop_btn)->child), fn);
443                 set_mix(1);
444         }       
445         loop_busy=0;
446         loop_win=NULL;
447         
448         set_disk_status(DISK_IDLE);
449 }
450
451 int scratch_destroy(GtkWidget *widget, GtkFileSelection *fs)
452 {
453         prelis_halt();
454         scratch_busy=0;
455         scratch_win=NULL;       
456         if (fs) gtk_widget_destroy(GTK_WIDGET(fs));
457         return(0);
458 }
459
460 int scratch_delete(GtkWidget *widget)
461 {
462         return(scratch_destroy(widget, NULL));
463 }
464
465 int loop_destroy(GtkWidget *widget, GtkFileSelection *fs)
466 {
467         prelis_halt();
468         loop_busy=0;
469         loop_win=NULL;  
470         if (fs) gtk_widget_destroy(GTK_WIDGET(fs));
471         return(0);
472 }
473
474 int loop_delete(GtkWidget *widget)
475 {
476         return(loop_destroy(widget, NULL));
477 }
478
479 void sel_change(GtkWidget *w)
480 {
481         GtkFileSelection *fs;
482         
483         fs=GTK_FILE_SELECTION(gtk_widget_get_toplevel(w));
484         
485         prelis_start(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
486
487 }
488
489
490 void select_scratch (GtkWidget *widget, gpointer data)
491 {
492         GtkWidget *fs;
493         
494         if (scratch_busy)
495         {
496                 gdk_window_raise(scratch_win);
497                 return;
498         }
499         
500         fs=gtk_file_selection_new("Select Scratch File");       
501         
502         if (strlen(last_scratch) >0)
503         {
504                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_scratch);
505         }
506         else
507         {
508                 if (strlen(globals.scratch_name) > 0)
509                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), globals.scratch_name);
510         }
511
512         gtk_widget_show(fs);
513         
514         
515         scratch_win=fs->window;         
516         scratch_busy=1;
517
518         gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", load_scratch, fs);
519         gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button), "clicked", GTK_SIGNAL_FUNC (scratch_destroy), fs);       
520         gtk_signal_connect (GTK_OBJECT(fs), "delete-event", GTK_SIGNAL_FUNC(scratch_delete), NULL);     
521         gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(fs)->file_list), "select_row", sel_change, fs);       
522 }
523
524
525 void select_loop (GtkWidget *widget, gpointer data)
526 {
527         GtkWidget *fs;
528
529         if (loop_busy)
530         {
531                 gdk_window_raise(loop_win);
532                 return;
533         }
534
535         fs=gtk_file_selection_new("Select Loop File");
536
537         if (strlen(last_loop) >0)
538         {
539                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), last_loop);
540         }
541         else
542         {
543                 if (strlen(globals.loop_name) > 0)
544                 gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), globals.loop_name);
545         }
546         
547         gtk_widget_show(fs);
548                 
549         loop_win=fs->window;            
550         loop_busy=1;
551                 
552         gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", load_loop, fs);
553         gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button), "clicked", GTK_SIGNAL_FUNC(loop_destroy), fs);   
554         gtk_signal_connect (GTK_OBJECT(fs), "delete-event", GTK_SIGNAL_FUNC(loop_delete), NULL);        
555         gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(fs)->file_list), "select_row", sel_change, fs);
556 }
557
558 void mix_clicked(GtkWidget *widget)
559 {
560         if (check_busy()) return;
561         
562         if (GTK_TOGGLE_BUTTON (widget)->active)
563         {
564                 set_mix(1);
565         }       
566         else
567         {
568                 set_mix(0);
569         }
570 }
571
572 void mix_changed (GtkWidget *widget)
573 {
574         f_prec value;
575         
576         value=(f_prec) GTK_ADJUSTMENT(widget)->value;
577
578         vttgl->vol_loop=value;
579         vttgl->vol_scratch=1.0-value;
580         globals.scratch_vol=1.0-value;
581 }
582
583 void quit()
584 {
585         globals.width=window->allocation.width;
586         globals.height=window->allocation.height;
587
588         gtk_main_quit();
589 }
590
591 void set_mode(GtkWidget *w)
592 {
593         if (GTK_TOGGLE_BUTTON(mode_normal)->active)
594         {
595                 vttgl->mode=MODE_SCRATCH;
596                 if (display_scratch)
597                 {
598                         display_scratch=0;
599                         gtk_tx_set_data(GTK_TX(wav_display), globals.scratch_data, globals.scratch_len);
600                 }                
601         }
602         else if (GTK_TOGGLE_BUTTON(mode_record)->active)
603         { 
604                 vttgl->mode=MODE_RECORD_SCRATCH;
605                 
606                 if (display_scratch)
607                 {
608                         display_scratch=0;
609                         gtk_tx_set_data(GTK_TX(wav_display), globals.scratch_data, globals.scratch_len);
610                 }                       
611         }
612         else if (GTK_TOGGLE_BUTTON(mode_playback)->active)
613         {
614                 vttgl->mode=MODE_PLAYBACK_RECORDED;
615
616                 if (!display_scratch)
617                 {
618                         display_scratch=1;
619                         gtk_tx_set_data(GTK_TX(wav_display), globals.rec_buffer, globals.rec_len);
620                 }                               
621         }
622 }
623
624 void display_engine_error(int err)
625 {
626         char buffer[2048]="Engine Error: ";
627         
628         if (err < ENG_ERR) return;
629         
630         switch (err)
631         {
632                 case ENG_ERR_XOPEN:
633                         strcat(buffer, "Couldn't connect to X-Display.");
634                         break;
635
636                 case ENG_ERR_XINPUT:
637                         strcat(buffer, "Couldn't open XInput device.");
638                         break;
639                         
640                 case ENG_ERR_DGA:
641                         strcat(buffer, "Failed to enable DGA (DirectMouse).");
642                         break;
643                         
644                 case ENG_ERR_SOUND:
645                         strcat(buffer, "Failed to open audio device.");
646                         break;
647
648                 case ENG_ERR_THREAD:
649                         strcat(buffer, "Failed to run engine thread.");
650                         break;
651                         
652                 case ENG_ERR_GRABMOUSE:
653                         strcat(buffer, "Couldn't grab the mouse.");
654                         break;
655
656                 case ENG_ERR_GRABKEY:
657                         strcat(buffer, "Couldn't grab the keyboard.");
658                         break;
659                         
660                 case ENG_ERR_BUSY:
661                         strcat(buffer, "Oops! Engine running already?");
662                         break;
663                         
664                 default:
665                         strcat(buffer, "Oops. Internal Error :(");
666         }
667         tx_note(buffer);
668 }
669
670 int action()
671 {
672         GtkWidget *top;
673         int ret;
674
675         if ((vttgl->mode==MODE_PLAYBACK_RECORDED) && (globals.rec_len<=0))
676         {
677                 tx_note("Error: Nothing recorded - nothing to playback ;)");
678                 return(0);
679         }
680         
681         if (!globals.scratch_data) 
682         {
683                 tx_note("Error: No scratch data availabe. Please load a sample to scratch on first.");
684                 return(0);
685         }
686
687         if ((globals.do_mix) && (!globals.loop_data))
688         {
689                 tx_note("Error: No mix data availabe. Disable mix.");
690                 return(0);
691         }
692
693         if (check_busy()) return(0);
694
695         if (disk_status)
696         {
697                 switch(disk_status)
698                 {
699                         case DISK_WRITING:
700                         tx_note("Fast User Warning: Please wait for terminatorX to finish writing to disk. Thanks!");
701                         break;
702                         
703                         case DISK_READING:
704                         tx_note("Fast User Warning: Please wait for terminatorX to finish reading from disk. Thanks!");                 
705                         break;
706                 }
707                 return(0);
708         }
709
710         if (!xwindow)
711         {
712                 top=gtk_widget_get_toplevel(window);
713                 xwindow=GDK_WINDOW_XWINDOW(top->window);
714         }
715
716         idle_ctr=0;
717         
718         ret=run_engine();
719         
720         if (!ret)
721         {
722                 engine_busy=1;
723                 gtk_tx_prepare_pos_display(GTK_TX(wav_display));
724                 idle_tag = gtk_timeout_add(globals.update_idle, (GtkFunction) pos_update, NULL);
725         }
726         else
727         {
728                 display_engine_error(ret);
729                 stop_engine();          
730         }
731         return(0);
732 }
733
734 int check_save_sanity(mix)
735 {
736         if (globals.rec_len==0) 
737         {
738                 tx_note("Save Error: Nothing recorded!");
739                 return(1);
740         }
741         
742         if ((mix) && (globals.loop_data==NULL))
743         {
744                 tx_note("Save Error: No loop file loaded. Try saving unmixed.");
745                 return(1);
746         }
747         
748         return(0);
749 }
750
751 void do_save_fast(GtkWidget *w, int* mixgl)
752 {
753         int mix=*mixgl;
754         char filename[PATH_MAX];
755         
756         if(check_save_sanity(mix)) return;
757
758         set_disk_status(DISK_WRITING);
759         
760         sprintf(filename, "%s%04i.wav", globals.prefix, globals.filectr);
761         globals.filectr++;
762         if (vtt_save(vttgl, filename, mix)) tx_note("Save Error: Error writing file.");
763
764         set_disk_status(DISK_IDLE);
765         
766 }
767
768 int save_destroy(GtkWidget *w, GtkFileSelection *fs)
769 {
770         save_win=NULL;
771         save_busy=0;
772         if (fs) gtk_widget_destroy(GTK_WIDGET(fs));
773         return(0);      
774 }
775
776 int save_delete(GtkWidget *w)
777 {
778         return (save_destroy(w, NULL));
779 }
780
781
782 void save(GtkWidget *w, GtkFileSelection *fs)
783 {
784         char newfile[PATH_MAX];
785         
786         if(check_save_sanity(1)) return;
787                 
788         set_disk_status(DISK_WRITING);  
789                 
790         strcpy(newfile, gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
791         if (strlen(newfile))
792         {
793                 if (vtt_save(vttgl, newfile, 0)) tx_note("Save Error: Error writing file.");    
794                 strcpy(globals.last_fn, newfile);               
795         }
796         save_destroy(w, fs);
797         
798         set_disk_status(DISK_IDLE);     
799 }
800
801 int save_mix_destroy(GtkWidget *w, GtkFileSelection *fs)
802 {
803         save_mix_win=NULL;
804         save_mix_busy=0;
805         if (fs) gtk_widget_destroy(GTK_WIDGET(fs));
806         return(0);      
807 }
808
809 int save_mix_delete(GtkWidget *w)
810 {
811         return (save_mix_destroy(w, NULL));
812 }
813
814 void save_mix(GtkWidget *w, GtkFileSelection *fs)
815 {
816         char newfile[PATH_MAX];
817         
818         if(check_save_sanity(1)) return;
819
820         set_disk_status(DISK_WRITING);
821         
822         strcpy(newfile, gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
823         if (strlen(newfile))
824         {
825                 if (vtt_save(vttgl, newfile, 1)) tx_note("Save Error: Error writing file.");    
826                 strcpy(globals.last_fn, newfile);               
827         }
828         save_mix_destroy(w, fs);
829
830         set_disk_status(DISK_IDLE);
831         
832 }
833
834 void do_save_as(GtkWidget *w, int* mixgl)
835 {
836         int mix=*mixgl;
837         GtkWidget *fs;
838         
839         if(check_save_sanity(mix)) return;
840         
841         if ((mix) && (save_mix_busy))
842         {
843                 gdk_window_raise(save_mix_win);
844                 return;
845         }
846
847         if ((!mix) && (save_busy))
848         {
849                 gdk_window_raise(save_win);
850                 return;
851         }
852
853         if (mix)
854         {       
855                 fs=gtk_file_selection_new("Save mixed Scratch");        
856         }
857         else
858         {
859                 fs=gtk_file_selection_new("Save Scratch");              
860         }
861         
862         
863         if (strlen(globals.last_fn) > 0)
864         gtk_file_selection_set_filename(GTK_FILE_SELECTION(fs), globals.last_fn);
865
866         gtk_widget_show(fs);
867         
868         if (!mix)
869         {       
870                 save_win=fs->window;            
871                 save_busy=1;
872         }
873         else
874         {
875                 save_mix_win=fs->window;                
876                 save_mix_busy=1;        
877         }
878
879         if (!mix)
880         {
881                 gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", save, fs);
882                 gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button), "clicked", GTK_SIGNAL_FUNC(save_destroy), fs);   
883                 gtk_signal_connect (GTK_OBJECT(fs), "delete-event", GTK_SIGNAL_FUNC(save_delete), NULL);        
884         }
885         else
886         {
887                 gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), "clicked", save_mix, fs);
888                 gtk_signal_connect (GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button), "clicked", GTK_SIGNAL_FUNC(save_mix_destroy), fs);       
889                 gtk_signal_connect (GTK_OBJECT(fs), "delete-event", GTK_SIGNAL_FUNC(save_mix_delete), NULL);    
890         }
891 }
892
893 void mplcfitx()
894 /* Most Proabably Least Called Function In Terminator X :) */
895 {
896         show_about(0);
897 }
898
899 void scratch_drop(GtkWidget *w)
900 {
901         puts("drop");
902 }
903
904 void lowpass_toggled(GtkWidget *widget)
905 {       
906         if (GTK_TOGGLE_BUTTON (widget)->active)
907         {
908                 globals.lowpass_enable=1;
909         }       
910         else
911         {
912                 globals.lowpass_enable=0;
913         }
914 }
915
916 void lowpass_reso_changed (GtkWidget *widget)
917 {
918         f_prec value;
919         
920         value=(f_prec) GTK_ADJUSTMENT(widget)->value;
921
922         vtt_lowpass_conf(vttgl, 1.0, value);
923         globals.lowpass_reso=value;
924 }
925
926 void setup_signals()
927 {
928         gtk_signal_connect (GTK_OBJECT(scratch_btn), "clicked", (GtkSignalFunc) select_scratch, NULL);
929         gtk_signal_connect (GTK_OBJECT(loop_btn), "clicked", (GtkSignalFunc) select_loop, NULL);
930         gtk_signal_connect (GTK_OBJECT(mix_toggle_btn), "clicked", (GtkSignalFunc) mix_clicked, NULL);
931         gtk_signal_connect (GTK_OBJECT(mix_adjustment), "value_changed", (GtkSignalFunc) mix_changed, NULL);
932         gtk_signal_connect (GTK_OBJECT(window), "destroy", (GtkSignalFunc) quit, NULL);
933         gtk_signal_connect (GTK_OBJECT(mode_normal), "clicked", (GtkSignalFunc) set_mode, NULL);
934         gtk_signal_connect (GTK_OBJECT(mode_record), "clicked", (GtkSignalFunc) set_mode, NULL);
935         gtk_signal_connect (GTK_OBJECT(mode_playback), "clicked", (GtkSignalFunc) set_mode, NULL);
936         gtk_signal_connect (GTK_OBJECT(action_btn), "clicked", (GtkSignalFunc) action, NULL);
937         gtk_signal_connect (GTK_OBJECT(options_btn), "clicked", (GtkSignalFunc) display_options, NULL);
938         gtk_signal_connect (GTK_OBJECT(save_fast), "clicked", (GtkSignalFunc) do_save_fast, &db_save_raw);      
939         gtk_signal_connect (GTK_OBJECT(save_fast_mix), "clicked", (GtkSignalFunc) do_save_fast, &db_save_mix);  
940         gtk_signal_connect (GTK_OBJECT(save_as), "clicked", (GtkSignalFunc) do_save_as, &db_save_raw);  
941         gtk_signal_connect (GTK_OBJECT(save_as_mix), "clicked", (GtkSignalFunc) do_save_as, &db_save_mix);      
942         gtk_signal_connect (GTK_OBJECT(about_btn), "clicked", (GtkSignalFunc) mplcfitx, NULL);  
943         gtk_signal_connect (GTK_OBJECT(scratch_btn), "drag-drop", (GtkSignalFunc) scratch_drop, NULL);
944         gtk_signal_connect (GTK_OBJECT(lowpass_toggle_btn), "clicked", (GtkSignalFunc) lowpass_toggled, NULL);
945         gtk_signal_connect (GTK_OBJECT(lowpass_freq), "value_changed", (GtkSignalFunc) lowpass_reso_changed, NULL);
946 }
947
948 void create_gui(int x, int y)
949 {
950         GtkWidget *label;
951         GtkWidget *separator;
952         GtkWidget *top_hbox;
953         GtkWidget *sub_vbox1;
954         GtkWidget *sub_vbox2;
955         GtkWidget *sub_sub_hbox;
956         GtkWidget *sub_sub_vbox;
957         char *fn;
958         
959         window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
960         gtk_window_set_title(GTK_WINDOW(window), VERSIONSTRING);
961         gtk_window_set_default_size(GTK_WINDOW(window), x, y);
962         
963         tooltips=gtk_tooltips_new();
964         
965         gtk_container_set_border_width(GTK_CONTAINER(window), 5);
966
967         mix_adjustment = (GtkAdjustment*) gtk_adjustment_new (1.0-globals.scratch_vol, 0, 1, 0.001, 0.01, 0.01);
968         
969         top_hbox = gtk_hbox_new(FALSE, 5);
970         
971         sub_vbox1 = gtk_vbox_new(FALSE, 5);
972
973         /* begin upper box */
974         
975         sub_sub_hbox = gtk_hbox_new(FALSE, 5);  
976         
977         label = gtk_label_new ("Scratch: ");
978         gtk_misc_set_alignment (GTK_MISC(label), 0.5 ,0.5);
979         gtk_box_pack_start (GTK_BOX(sub_sub_hbox), label, WID_FIX);
980         gtk_widget_show(label);
981         
982         if (strlen(globals.scratch_name))
983         {
984                 fn=strrchr(globals.scratch_name, '/');
985                 if (fn) fn++;
986                 else fn=globals.scratch_name;
987                 scratch_btn = gtk_button_new_with_label (fn);
988         }
989         else
990         {
991                 scratch_btn = gtk_button_new_with_label ("<NONE>");     
992         }
993         gtk_box_pack_start (GTK_BOX(sub_sub_hbox), scratch_btn, WID_DYN);
994         gtk_tooltips_set_tip(tooltips, scratch_btn, "Click here to load a new wave-file to scratch on.", NULL);
995         gtk_widget_show(scratch_btn);   
996         
997         mix_toggle_btn = gtk_check_button_new_with_label ("Mix");
998         gtk_box_pack_start (GTK_BOX(sub_sub_hbox), mix_toggle_btn, WID_FIX);
999         gtk_tooltips_set_tip(tooltips, mix_toggle_btn, "Click here to en-/disable mixing of loop wave-file.", NULL);    
1000         gtk_widget_show(mix_toggle_btn);
1001         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mix_toggle_btn), globals.do_mix);
1002         
1003         mix_slider = gtk_hscale_new(mix_adjustment);
1004         gtk_widget_set_usize(mix_slider, 100, 0);
1005         gtk_scale_set_draw_value(GTK_SCALE(mix_slider), 0);
1006         gtk_box_pack_start (GTK_BOX(sub_sub_hbox), mix_slider, WID_DYN);
1007         gtk_tooltips_set_tip(tooltips, mix_slider, "Select mixing ratio scratch/loop.", NULL);  
1008         gtk_widget_show(mix_slider);    
1009                         
1010         label = gtk_label_new ("Loop: ");
1011         gtk_misc_set_alignment (GTK_MISC(label), 0.5 ,0.5);
1012         gtk_box_pack_start (GTK_BOX(sub_sub_hbox), label, WID_FIX);
1013         gtk_widget_show(label);
1014         
1015         if (strlen(globals.loop_name))
1016         {
1017                 fn=strrchr(globals.loop_name, '/');
1018                 if (fn) fn++;
1019                 else fn=globals.loop_name;
1020                 loop_btn = gtk_button_new_with_label (fn);
1021         }
1022         else
1023         {
1024                 loop_btn = gtk_button_new_with_label ("<NONE>");        
1025         }
1026         gtk_box_pack_start (GTK_BOX(sub_sub_hbox), loop_btn, WID_DYN);
1027         gtk_tooltips_set_tip(tooltips, loop_btn, "Click here to load a new wave-file as loop.", NULL);
1028         gtk_widget_show(loop_btn);      
1029         
1030         gtk_box_pack_start(GTK_BOX(sub_vbox1), sub_sub_hbox, WID_FIX);
1031         gtk_widget_show(sub_sub_hbox);
1032         
1033         /* end upper box */     
1034         
1035         separator = gtk_hseparator_new ();
1036         gtk_box_pack_start(GTK_BOX(sub_vbox1), separator, WID_FIX);
1037         gtk_widget_show(separator);
1038         
1039         /* begin lower left box */
1040         
1041         sub_sub_vbox = gtk_vbox_new(FALSE, 5);
1042         
1043         wav_display = gtk_tx_new (globals.scratch_data, globals.scratch_size/2);
1044         gtk_box_pack_start (GTK_BOX(sub_sub_vbox), wav_display, WID_DYN);
1045         gtk_widget_show(wav_display);   
1046
1047         wav_progress = gtk_progress_bar_new();
1048         gtk_box_pack_start (GTK_BOX(sub_sub_vbox), wav_progress, WID_FIX);
1049         gtk_widget_show(wav_progress);
1050
1051         /* end lower left box */
1052
1053         gtk_box_pack_start(GTK_BOX(sub_vbox1), sub_sub_vbox, WID_DYN);
1054         gtk_widget_show(sub_sub_vbox);
1055
1056         
1057         /* begin lower right box */
1058
1059         sub_vbox2 = gtk_vbox_new(TRUE, 5);
1060
1061         label = gtk_label_new ("Operation:");
1062         gtk_misc_set_alignment (GTK_MISC(label), 0.5 ,0.5);
1063         gtk_box_pack_start (GTK_BOX(sub_vbox2), label, WID_FIX);
1064         gtk_widget_show(label); 
1065         
1066         mode_normal = gtk_radio_button_new_with_label(NULL, "Free Scratch");
1067         gtk_tooltips_set_tip(tooltips, mode_normal, "Click here to enable \"normal\", unlimited scratching.", NULL);    
1068         gtk_box_pack_start (GTK_BOX(sub_vbox2), mode_normal, WID_FIX);
1069         gtk_widget_show(mode_normal);   
1070         
1071         mode_radios = gtk_radio_button_group( GTK_RADIO_BUTTON(mode_normal) );
1072         
1073         mode_record = gtk_radio_button_new_with_label(mode_radios, "Record Scratch");
1074         gtk_box_pack_start (GTK_BOX(sub_vbox2), mode_record, WID_FIX);
1075         gtk_tooltips_set_tip(tooltips, mode_record, "Click here to enable recording to record buffer.", NULL);  
1076         gtk_widget_show(mode_record);   
1077
1078         mode_radios = gtk_radio_button_group( GTK_RADIO_BUTTON(mode_record) );
1079                 
1080         mode_playback = gtk_radio_button_new_with_label(mode_radios, "Playback Scratch");
1081         gtk_box_pack_start (GTK_BOX(sub_vbox2), mode_playback, WID_FIX);        
1082         gtk_tooltips_set_tip(tooltips, mode_playback, "Click here to enable playback of your recorded scratches.", NULL);               
1083         gtk_widget_show(mode_playback); 
1084         
1085         action_btn = gtk_button_new_with_label ("Start");
1086         gtk_box_pack_start (GTK_BOX(sub_vbox2), action_btn, WID_FIX);   
1087         gtk_tooltips_set_tip(tooltips, action_btn, "Click here to activate audio.", NULL);              
1088         gtk_widget_show(action_btn);    
1089
1090         separator = gtk_hseparator_new ();
1091         gtk_box_pack_start(GTK_BOX(sub_vbox2), separator, WID_FIX);
1092         gtk_widget_show(separator);
1093         
1094         lowpass_toggle_btn = gtk_check_button_new_with_label ("LP Filter");
1095         gtk_box_pack_start (GTK_BOX(sub_vbox2), lowpass_toggle_btn, WID_FIX);
1096         gtk_tooltips_set_tip(tooltips, mix_toggle_btn, "Click here to en-/disable the lowpass filter.", NULL);  
1097         gtk_widget_show(lowpass_toggle_btn);
1098         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lowpass_toggle_btn), globals.lowpass_enable);
1099
1100         lowpass_freq = (GtkAdjustment*) gtk_adjustment_new (globals.lowpass_reso, 0, 0.95, 0.001, 0.001, 0.01);
1101         
1102         lowpass_slider = gtk_hscale_new(lowpass_freq);
1103         gtk_scale_set_draw_value(GTK_SCALE(lowpass_slider), 0);
1104         gtk_box_pack_start (GTK_BOX(sub_vbox2), lowpass_slider, WID_FIX);
1105         gtk_tooltips_set_tip(tooltips, lowpass_slider, "This sets the resonance parameter of the lowpass filter.", NULL);       
1106         gtk_widget_show(lowpass_slider);        
1107                 
1108         separator = gtk_hseparator_new ();
1109         gtk_box_pack_start(GTK_BOX(sub_vbox2), separator, WID_FIX);
1110         gtk_widget_show(separator);
1111         
1112         save_fast = gtk_button_new_with_label ("Save Fast");
1113         gtk_box_pack_start (GTK_BOX(sub_vbox2), save_fast, WID_FIX);    
1114         gtk_tooltips_set_tip(tooltips, save_fast, "Click here to save your recorded scratches without giving a name.", NULL);           
1115         gtk_widget_show(save_fast);     
1116
1117         save_fast_mix = gtk_button_new_with_label ("Save Fast (mix)");
1118         gtk_tooltips_set_tip(tooltips, save_fast_mix, "Click here to save your recorded scratches (mixed with the loop) without giving a name.", NULL);         
1119         gtk_box_pack_start (GTK_BOX(sub_vbox2), save_fast_mix, WID_FIX);        
1120         gtk_widget_show(save_fast_mix); 
1121
1122         save_as = gtk_button_new_with_label ("Save As");
1123         gtk_box_pack_start (GTK_BOX(sub_vbox2), save_as, WID_FIX);      
1124         gtk_tooltips_set_tip(tooltips, save_as, "Click here to save your recorded scratches after selecting a filename.", NULL);                        
1125         gtk_widget_show(save_as);       
1126
1127         save_as_mix = gtk_button_new_with_label ("Save As (mix)");
1128         gtk_box_pack_start (GTK_BOX(sub_vbox2), save_as_mix, WID_FIX);  
1129         gtk_tooltips_set_tip(tooltips, save_as_mix, "Click here to save your recorded scratches (mixed with the loop) after selecting a filename.", NULL);                      
1130         gtk_widget_show(save_as_mix);   
1131
1132         separator = gtk_hseparator_new ();
1133         gtk_box_pack_start(GTK_BOX(sub_vbox2), separator, WID_FIX);
1134         gtk_widget_show(separator);
1135
1136         options_btn = gtk_button_new_with_label ("Options");
1137         gtk_box_pack_start (GTK_BOX(sub_vbox2), options_btn, WID_FIX);  
1138         gtk_tooltips_set_tip(tooltips, options_btn, "Click here to setup options. (And to disable tooptips ;)", NULL);                          
1139         gtk_widget_show(options_btn);   
1140         
1141         about_btn = gtk_button_new_with_label ("About / License");
1142         gtk_box_pack_start (GTK_BOX(sub_vbox2), about_btn, WID_FIX);    
1143         gtk_tooltips_set_tip(tooltips, about_btn, "Click here to learn more about terminatorX.", NULL);                         
1144         gtk_widget_show(about_btn);     
1145
1146         separator = gtk_hseparator_new ();
1147         gtk_box_pack_start(GTK_BOX(sub_vbox2), separator, WID_FIX);
1148         gtk_widget_show(separator);
1149         
1150         pos_time = gtk_button_new_with_label ("00:00.00");
1151 //      gtk_misc_set_alignment (GTK_MISC(pos_time), 0.5 ,0.5);
1152         gtk_box_pack_start (GTK_BOX(sub_vbox2), pos_time, WID_FIX);
1153         gtk_widget_show(pos_time);      
1154         
1155         /* end lower right box */
1156         
1157         gtk_box_pack_start(GTK_BOX(top_hbox), sub_vbox1, WID_DYN);
1158         gtk_widget_show(sub_vbox1);
1159         
1160         separator = gtk_vseparator_new ();
1161         
1162         gtk_box_pack_start(GTK_BOX(top_hbox), separator, WID_FIX);
1163         gtk_widget_show(separator);
1164
1165         gtk_box_pack_start(GTK_BOX(top_hbox), sub_vbox2, WID_FIX);
1166         gtk_widget_show(sub_vbox2);
1167                 
1168         gtk_container_add(GTK_CONTAINER(window), top_hbox);
1169         
1170         gtk_widget_show(top_hbox);
1171
1172         setup_signals();
1173
1174 }
1175
1176 void display_gui()
1177 {
1178         if (globals.tooltips) gtk_tooltips_enable(tooltips);
1179         else gtk_tooltips_disable(tooltips);
1180
1181         gtk_widget_show(window);
1182         /* ARRGH!! 
1183         */
1184 }
1185
1186 void note_destroy(GtkWidget *widget, GtkWidget *mbox)
1187 {
1188         gtk_widget_destroy(mbox);
1189 }
1190
1191 void tx_note(char *message)
1192 {
1193         char buffer[4096]="\n     [ terminatorX Message: ]     \n\n";
1194         
1195         GtkWidget *mbox;
1196         GtkWidget *label;
1197         GtkWidget *btn;
1198         
1199         mbox=gtk_dialog_new();
1200
1201         strcat(buffer, "   ");  
1202         strcat(buffer, message);
1203         strcat(buffer, "   ");
1204         label=gtk_label_new(buffer);
1205         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mbox)->vbox), label, TRUE, TRUE, 0);
1206         gtk_widget_show(label);
1207         
1208         btn = gtk_button_new_with_label("Ok");
1209         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mbox)->action_area), btn, TRUE, TRUE, 0);
1210         gtk_widget_show(btn);
1211         
1212         gtk_signal_connect(GTK_OBJECT(btn), "clicked", note_destroy, mbox);
1213         
1214         gtk_widget_show(mbox);
1215 }
1216
1217 gfloat old_percent=-1;
1218
1219 void wav_progress_update(gfloat percent)
1220 {
1221         percent=floor(percent*10.0)/10.0; //Updateing statusbars with gtk-themes eats up hell of a lot CPU-time
1222                                           // which is why we update every 10% only.
1223         
1224         if (wav_progress)
1225         {
1226                 if (old_percent != percent)
1227                 {
1228                         old_percent = percent;
1229                         gtk_progress_bar_update(GTK_PROGRESS_BAR(wav_progress), percent);
1230                         while (gtk_events_pending()) gtk_main_iteration();      
1231                 }
1232         }
1233
1234 }
1235
1236 #ifdef __cplusplus
1237 }
1238 #endif /* __cplusplus */