1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17 ////////////////////////////////////////////////////////////
18 // main.c, by Bret Logan (c) 2010
19 // Comprises Gnaural by uniting all the peripheral files. This is
20 // where any code should go that ties together the different modules
21 // (ScheduleGUI, ScheduleXML, BinauralBeat, Gnauralnet, etc.) and the
22 // GUI; they should not require any reference to each other directly.
23 ////////////////////////////////////////////////////////////
24
25 //TODO:
26 // - perhaps conglomerate glade and audio file searches to on function in order to prioritize relative paths
27 // - add "Truncated from start" sort of command in addition to the current truncate-from-end
28 // - implement MP3 functionality for files opened at command line; can't currently find file so opens default
29 // - BUG: when writing WAV to stdout (buggy CMDLINE generally), I pump out so much DBG info that the stream is polluted!
30 // - Make properties box check for all properties whether all DPs have them or not
31 // - add balance control to Properties box (no backdoor way to do it now via properties volumes; in fact, they ruin any preexisting balance relationships)
32 // - Make GUI "Schedule Info" text NOT center justified, and allow description to be in a scrollable box so it can be long
33 //BUGS:
34 // - [FIXED?] Add New Voice sometime checks the Mute box (but isn't even muted) [20100614: may have fixed this, see BB_LoadDefaultVoice()]
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39
40 #include <string.h> //needed for memcpy
41 #include <unistd.h> //needed for path/file setup: getcwd, getopt
42 #include <errno.h> //needed for path/file setup
43 #include <signal.h> //needed for SIGINT
44 #include <sys/stat.h> //needed for path/file setup: main_testfileforexistance
45 #include <stdlib.h>
46 #include <math.h> //for fabs()
47 #include <ctype.h> //for toupper
48 #include <glib.h>
49 #include <gtk/gtk.h>
50 #include <gdk/gdkkeysyms.h> //needed for keyboard translations
51 #include <sndfile.h>
52 #include <portaudio.h>
53 #include <locale.h>
54 #include "gettext.h"
55
56 #include "callbacks.h"
57 #include "ScheduleGUI.h"
58 #include "ScheduleXML.h"
59 #include "gnauralXML.h"
60 #include "BinauralBeat.h"
61 #include "main.h"
62 #include "main_ArrayConstants.h"
63 #include "playAudio.h"
64 #include "exportAudio.h"
65 #include "gnauralnet.h"
66 #include "voiceGUI.h"
67 #include "gnauralRecentMenu.h"
68 #include "gnauralVU.h"
69
70 #ifdef GNAURAL_WIN32
71 #include <windows.h> //for win32
72 #include <mmsystem.h> //for sound on win32
73 #include <io.h> //for binary-mode stdout on Win32
74 #include <fcntl.h> //for binary-mode stdout on Win32
75 #include <process.h> //for _spawn on Win32
76 #define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
77 #endif
78
79 //critical assignment for installation stage:
80 #define GLADE_FILE_NAME "gnaural.glade"
81 #define GLADE_FILE PACKAGE_DATA_DIR"/"PACKAGE"/"GLADE_FILE_NAME
82
83 //20100528: on where linux distros will install presets:
84 #define GNAURAL_PRESET_DIR PACKAGE_DATA_DIR"/"PACKAGE"/presets"
85 #define GNAURAL_FILEFILTERSTRING "~Gnaural Files~*.gnaural,~Text Files~*.txt,~All Files~,*"
86 #define GNAURAL_GUI_UPDATEINTERVAL 128 //64
87
88 //drag'n'drop stuff
89 //very basic, treats anything dropped like a string
90 enum
91 {
92 DND_TARGET_STRING,
93 };
94
95 static GtkTargetEntry targetentries[] = {
96 {"text/plain", 0, DND_TARGET_STRING}, //allows text to be dropped
97 {"text/uri-list", 0, DND_TARGET_STRING}, //allows a file to be dropped
98 };
99
100 //end drag'n'drop stuff
101
102 //START Need two globals for AudioFileData.
103 //All Audio files get stored here, and are indexed by their filename
104 typedef struct main_AFData_type
105 {
106 int *PCM_data;
107 int PCM_data_size; //holds the number of elements in PCM_data;
108 char *filename;
109 } main_AFData;
110 static main_AFData *main_AudioFileData = NULL;
111 static int main_AudioFileData_count = 0; //always holds number of main_AudioFileData elements
112
113 //START Globals taken from ScheduleGUI:
114 GdkPixmap *main_pixmap_Graph; //main drawing area; must be created by external code (i.e., a main.cpp), then set in SG_Init()
115 char *main_Info_Description = NULL;
116 char *main_Info_Author = NULL;
117 char *main_Info_Title = NULL;
118 GtkWidget *main_window = NULL;
119 GtkWidget *main_vpanedListGraph = NULL;
120 GtkWidget *main_frameVoices = NULL;
121 GtkVBox *main_vboxVoices = NULL;
122 GtkWidget *main_drawing_area = NULL;
123 GtkWidget *main_ProgressBar = NULL;
124 GtkButton *main_buttonPlayPause = NULL;
125 guint GUIUpdateHandle = 0; //handle to the timer that updates the GUI every GNAURAL_GUI_UPDATEINTERVAL ms.
126 char *main_AudioWriteFile_name = NULL; //this gets alloted a default name in main();, NEVER equals NULL
127 GThread *main_AudioWriteFile_thread = NULL; //20071201 this will equal NULL if a file is NOT being written to
128 int main_AudioWriteFile_format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; //this is merely a default
129 int gnaural_pauseflag = (~0); //pauseflag controls all movement through schedule, also stops and starts sound
130 GtkLabel *main_labelFileName = NULL;
131 GtkLabel *main_labelFileDescription = NULL;
132 GtkLabel *main_labelFileTitle = NULL;
133 GtkLabel *main_labelFileAuthor = NULL;
134 GtkLabel *main_labelStatus = NULL;
135 GtkLabel *main_labelTotalRuntime = NULL;
136 GtkLabel *main_labelCurrentRuntime = NULL;
137 GtkEntry *main_entryLoops = NULL;
138 GtkToggleButton *main_togglebuttonViewFreq = NULL;
139 GtkToggleButton *main_togglebuttonViewBeat = NULL;
140 GtkToggleButton *main_togglebuttonViewVol = NULL;
141 GtkToggleButton *main_togglebuttonViewBal = NULL;
142 GtkStatusbar *main_Statusbar = NULL;
143 GtkHScale *main_hscaleVolume = NULL;
144 GtkHScale *main_hscaleBalance = NULL;
145 GtkToggleButton *main_checkbuttonSwapStereo = NULL;
146 GtkToggleButton *main_checkbuttonOutputMono = NULL; //20100405
147 gboolean main_KeyDown = FALSE; //a way to keep track of start of arrow event in order to backup only once per mass event
148 char main_sTmp[PATH_MAX + PATH_MAX + 1]; //just a place to store tmp strings
149 const char main_sSec[] = "sec ";
150 const char main_sMin[] = "min ";
151 const char main_sInf[] = "inf";
152
153 const char main_sBinauralBeat[] = "Binaural Beat";
154 const char main_sPinkNoise[] = "Pink Noise";
155 const char main_sAudioFile[] = "Audio File";
156 const char main_sIsochronicPulses[] = "Isochronic Pulses";
157 const char main_sIsochronicPulses_alt[] = "Alt Isochronic Pulses";
158 const char main_sWaterDrops[] = "Water Drops";
159 const char main_sRain[] = "Rain";
160
161 //the only purpose of this is to help load old-style Gnaural files:
162 struct
163 {
164 float FreqBase;
165 float Volume_Tone;
166 float Volume_Noiz;
167 int loops;
168 int loopcount;
169 int ScheduleEntriesCount;
170 }
171 OldGnauralVars;
172
173 //path and filename variables:
174 char main_sPathCurrentWorking[PATH_MAX + 1]; //whatever directory user called gnaural from
175 char main_sPathHome[PATH_MAX + 1]; // ~/ -- not meaningfull in Win32
176 char main_sPathGnaural[PATH_MAX + 1]; // ~/.gnaural/ -- not meaningfull in Win32
177 char main_sPathTemp[PATH_MAX + PATH_MAX + 1]; //used to copy the other paths and strcat filenames-to
178 gchar *main_sPathExecutable = NULL; //so program can call itself to make MP3
179 gchar main_sCurrentGnauralFilenameAndPath[PATH_MAX + 1]; // ~/.gnaural/schedule.gnaural
180
181 //my getopt() vars/flags:
182 #define GNAURAL_CMDLINEOPTS ":w:a:sodpih"
183 int cmdline_w = 0; //Output .WAV file directly to file
184 int cmdline_a = 0; //tell Gnaural which sound HW to access
185 int cmdline_s = 0; //Create fresh Schedule file flag
186 int cmdline_o = 0; //Output .WAV directly to stdout
187 int cmdline_d = 0; //Show debugging information
188 int cmdline_p = 0; //Output to sound system
189 int cmdline_i = 0; //Show detailed console information
190 // int cmdline_h = 0; //show help [not actually needed, since it is handled during parsing]
191
192 int main_gnaural_guiflag = FALSE; //will be set internall to TRUE if there is a GUI
193 float main_OverallVolume = 1.f; //this solely mirrors the OverallVolume slider; don't set
194 float main_OverallBalance = 0.f; //this solely mirrors the OverallBalance slider; don't set
195 gboolean main_vscale_Y_mousebuttondown = FALSE;
196 float main_vscale_Y = 0.0f;
197 gboolean main_hscale_X_mousebuttondown = FALSE;
198 float main_hscale_X = 0.0f;
199 gboolean main_XY_scaleflag = FALSE;
200 gboolean main_MagneticPointerflag = FALSE; //added 20101006
201
202 /////////////////////////////////////////////////////
main(int argc,char * argv[])203 int main (int argc, char *argv[])
204 {
205 BB_SeedRand (3676, 2676862); //anything but 0 on either or twins seems OK
206 BB_UserSleep = main_Sleep;
207 #ifdef ENABLE_NLS
208 setlocale (LC_ALL, "");
209 bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
210 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
211 textdomain (GETTEXT_PACKAGE);
212 //setlocale (LC_ALL, "");
213 //bindtextdomain (PACKAGE, LOCALEDIR);
214 //textdomain (PACKAGE);
215 #endif
216
217 #ifdef GNAURAL_WIN32
218 //this ensures that any potential Windows stdout activity is in binary mode:
219 _setmode (_fileno (stdout), O_BINARY);
220 // _setmode (_fileno (stdin), O_BINARY);
221 #endif
222
223 //setup a few globals:
224 main_sPathExecutable = argv[0]; //so program can call itself to make MP3, among other things
225 main_sCurrentGnauralFilenameAndPath[0] = '\0'; //this tells main_SetupPathsAndFiles to use default filename
226 SG_StringAllocateAndCopy (&main_AudioWriteFile_name, "Gnaural");
227
228 main_ResetScheduleInfo ();
229
230 //do command line parsing:
231 main_ParseCmdLine (argc, argv);
232
233 //Take care of stuff joint to command-line and GUI first:
234 //trap Ctrl-C:
235 if (signal (SIGINT, SIG_IGN) != SIG_IGN)
236 {
237 signal (SIGINT, main_InterceptCtrl_C);
238 }
239
240 //next need to set up paths, since bb (below) will need to know
241 //(among other things) where to access/create gnaural_schedule.txt file:
242 main_SetupPathsAndFiles ((main_sCurrentGnauralFilenameAndPath[0] ==
243 '\0') ? TRUE : FALSE);
244
245 //see if file exists; if not, write it:
246 if (0 != main_TestPathOrFileExistence (main_sCurrentGnauralFilenameAndPath))
247 {
248 main_WriteDefaultFile ();
249 }
250
251 //Init GDK Threads, needed for file writing:
252 //added 20051126 to support GThread cross-compatibility. Yes, it is supposed to be called before gtk_init()
253 // call echo `pkg-config --libs gthread-2.0` to see the libs to link to; I was segfaulting endlessly because I was linking
254 //with old libs! This is the proper:
255 //g++ -g -O2 -o gnaural main.o BinauralBeat.o support.o interface.o callbacks.o pa_lib.o pa_unix.o pa_unix_oss.o -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangoxft-1.0 -lpangox-1.0 -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0 -pthread -lgthread-2.0
256 //if (!g_thread_supported ()) g_thread_init (NULL);
257 g_thread_init (NULL);
258 gdk_threads_init ();
259 //next line was moved below on 20071126 as per GTK+ FAQ example
260 //gdk_threads_enter (); //if I don't do this, the Win32 version blocks! The documentation is amazingly vague.
261
262 //init GTK:
263 gtk_set_locale ();
264 gtk_init (&argc, &argv);
265
266 main_SetIcon (); //added 20051202; bruteforce method to avoid nasty GTK path/function inconsistencies
267
268 //NOTE: even for command line, this needs to be here:
269 //this returns non-0 if it doesn't find the file gnaural.glade:
270 if (0 != main_InitGlade ())
271 {
272 main_Cleanup ();
273 return 0;
274 }
275
276 //open user file and put it in BB:
277 gxml_XMLReadFile (main_sCurrentGnauralFilenameAndPath, main_drawing_area,
278 FALSE);
279
280 //sort the args:
281 //The main question: Do I need a GUI? Basically boils down to these two categories:
282 //THINGS AFFECTING BOTH GUI AND TERM VERSION:
283 //- cmdline_a: Tell Gnaural which sound HW to use
284 //- cmdline_d: Show debugging information
285 //THINGS AFFECTING SOLEY COMMANDLINE VERSION:
286 //- cmdline_h: Print "Help" [already did this by this point] DOES NOT REQUIRE SOUND SYSTEM
287 //- cmdline_o: dump a .WAV file to sdtout DOES NOT REQUIRE SOUND SYSTEM
288 //- cmdline_w: dump a .WAV file to a file DOES NOT REQUIRE SOUND SYSTEM
289 //- cmdline_s: create a fresh gnaural_schedule.txt file DOES NOT REQUIRE SOUND SYSTEM
290 //- cmdline_i: show terminal-style GUI info
291 //- cmdline_p: run the schedule directly through the soundsystem
292 if ((cmdline_i + cmdline_p + cmdline_o + cmdline_w + cmdline_s) > 0)
293 { //do the command line version then exit:
294 SG_DBGOUT ("Entering console mode");
295 main_gnaural_guiflag = FALSE;
296 main_RunCmdLineMode ();
297 main_Cleanup ();
298 exit (EXIT_SUCCESS);
299 }
300
301 //got here, so must be using GUI:
302 main_gnaural_guiflag = TRUE;
303
304 //init the sound:
305 main_playAudio_SoundInit ();
306
307 //start stopped:
308 main_OnButton_Stop (NULL);
309
310 //Now we can update some GUI stuff:
311 //set graph view:
312 main_SetGraphToggle (main_drawing_area);
313 main_UpdateGUI_FileInfo (main_sCurrentGnauralFilenameAndPath);
314 main_UpdateGUI_Voices (main_vboxVoices);
315
316 //setup the main GUI timer:
317 GUIUpdateHandle =
318 g_timeout_add (GNAURAL_GUI_UPDATEINTERVAL, main_UpdateGUI, NULL);
319
320 //this is a hack, but does silence an unsightly gdk_draw error:
321 main_configure_event (main_window, NULL);
322
323 //do drag'n'drop stuff:
324 gtk_drag_dest_set (main_window, GTK_DEST_DEFAULT_ALL, targetentries,
325 G_N_ELEMENTS (targetentries), GDK_ACTION_COPY);
326
327 g_signal_connect (main_window, "drag_data_received",
328 G_CALLBACK (main_OnDragDataReceived), NULL);
329 //end drag'n'drop
330
331 //enters this until gtk_main_quit() is called
332 gdk_threads_enter (); //essential to do in a threaded GTK+ program
333 gtk_main ();
334
335 //flasher_Cleanup (main_FD);//disconnected 20071022
336 main_Cleanup ();
337 return 0;
338 }
339
340 /////////////////////////////////////////////////////
main_Cleanup()341 void main_Cleanup ()
342 {
343 //turn off gnauralnet thread:
344 GN_stop ();
345
346 //cleanup any write threads if active:
347 if (NULL != main_AudioWriteFile_thread)
348 {
349 BB_DBGOUT ("Aborting audio write file thread");
350 BB_WriteStopFlag = TRUE;
351 while (NULL != main_AudioWriteFile_thread)
352 {
353 main_Sleep (G_USEC_PER_SEC);
354 }
355 }
356 //clean up sound resources:
357 playAudio_SoundCleanup ();
358
359 //cleanup BB:
360 BB_CleanupVoices ();
361
362 //cleanup SG. NOTE: this should have been called once already at main_quit(), but to be sure:
363 SG_Cleanup ();
364
365 if (main_AudioWriteFile_name != NULL)
366 {
367 free (main_AudioWriteFile_name);
368 main_AudioWriteFile_name = NULL;
369 }
370
371 //cleansup the globals:
372 if (main_Info_Title != NULL)
373 {
374 free (main_Info_Title);
375 main_Info_Title = NULL;
376 }
377 if (main_Info_Description != NULL)
378 {
379 free (main_Info_Description);
380 main_Info_Description = NULL;
381 }
382 if (main_Info_Author != NULL)
383 {
384 free (main_Info_Author);
385 main_Info_Author = NULL;
386 }
387
388 if (NULL != main_AudioFileData)
389 {
390 main_CleanupAudioFileData ();
391 }
392
393 //don't forget to do this!:
394 BB_DBGOUT ("Calling gdk_threads_leave");
395 gdk_threads_leave ();
396 }
397
398 /////////////////////////////////////////
399 //a20070812
main_CleanupAudioFileData()400 void main_CleanupAudioFileData ()
401 {
402 //SG_DBGOUT_INT("Cleaning up BB:", BB_VoiceCount);
403 SG_DBGOUT_INT ("NULL'ing all PCM data in BB, voices:", BB_VoiceCount);
404 BB_NullAllPCMData (); //have to do this, unfortunately
405 SG_DBGOUT_INT ("Cleaning up Audio Data, filecount:",
406 main_AudioFileData_count);
407 int i;
408
409 for (i = 0; i < main_AudioFileData_count; i++)
410 {
411 main_AudioFileData[i].PCM_data_size = 0;
412 if (NULL != main_AudioFileData[i].PCM_data)
413 {
414 SG_DBGOUT_INT ("Cleaning up Audio Data, array", i);
415 free (main_AudioFileData[i].PCM_data);
416 main_AudioFileData[i].PCM_data = NULL;
417 }
418 if (NULL != main_AudioFileData[i].filename)
419 {
420 free (main_AudioFileData[i].filename);
421 main_AudioFileData[i].filename = NULL;
422 }
423 }
424 main_AudioFileData_count = 0;
425 SG_DBGOUT ("Cleaning up last bit of Audio Data");
426 if (NULL != main_AudioFileData)
427 {
428 free (main_AudioFileData);
429 main_AudioFileData = NULL;
430 }
431 }
432
433 /////////////////////////////////////////
434 //a20070702: to cleanup initialization
435 //returns 0 on success
main_InitGlade()436 int main_InitGlade ()
437 {
438 GError *error = NULL;
439 GtkBuilder *xml = gtk_builder_new ();
440 gchar *errstr = "Couldn't load GtkBuilder file: ";
441
442 //First task: find the glade file. Search Priority:
443 // 1) check cwd (since user may want to use a custom one)
444 // 2) try "dist installation location", as per PACKAGE_DATA_DIR
445 // 3) try the executable's dir
446 SG_DBGOUT ("Start search for Glade file");
447 SG_DBGOUT_STR ("Looking in cwd for:", GLADE_FILE_NAME);
448 SG_DBGOUT_STR ("getcwd():", getcwd (main_sTmp, sizeof main_sTmp));
449 SG_DBGOUT_STR ("realpath(getcwd):", realpath (main_sTmp, main_sPathTemp));
450
451 if (!g_file_test (GLADE_FILE_NAME, G_FILE_TEST_EXISTS) ||
452 0 == (gtk_builder_add_from_file (xml, GLADE_FILE_NAME, &error)))
453 {
454 //failed so trying again:
455 if (NULL != error)
456 {
457 g_warning ("%s%s", errstr, error->message);
458 g_error_free (error);
459 error = NULL;
460 }
461 SG_DBGOUT_STR
462 ("No valid GtkBuilder file there, trying configured install path:",
463 GLADE_FILE);
464 if (!g_file_test (GLADE_FILE, G_FILE_TEST_EXISTS)
465 || 0 == (gtk_builder_add_from_file (xml, GLADE_FILE, &error)))
466 {
467 //failed so trying again:
468 if (NULL != error)
469 {
470 g_warning ("%s%s", errstr, error->message);
471 g_error_free (error);
472 error = NULL;
473 }
474 //failed so trying again:
475 if (NULL == realpath (main_sPathExecutable, main_sTmp))
476 {
477 SG_DBGOUT_STR ("realpath returned NULL for ", main_sPathExecutable);
478 g_strlcpy (main_sTmp, main_sPathExecutable,
479 strlen (main_sPathExecutable));
480 }
481 //SG_DBGOUT_STR ("argv[0] is ", main_sPathExecutable);
482 //SG_DBGOUT_STR ("argv[0] realpath is ", main_sTmp);
483 gchar *tmpstr1 = g_path_get_dirname (main_sTmp);
484 gchar *tmpstr2 =
485 g_strconcat (tmpstr1, G_DIR_SEPARATOR_S, GLADE_FILE_NAME, NULL);
486 g_free (tmpstr1);
487 SG_DBGOUT_STR ("Not there, trying app's dir:", tmpstr2);
488 if (!g_file_test (tmpstr2, G_FILE_TEST_EXISTS) ||
489 0 == (gtk_builder_add_from_file (xml, tmpstr2, &error)))
490 {
491 //failed so trying again:
492 if (NULL != error)
493 {
494 g_warning ("%s%s", errstr, error->message);
495 g_error_free (error);
496 error = NULL;
497 }
498 SG_ERROUT ("Couldn't find glade file locally, ");
499 return -1;
500 }
501 g_free (tmpstr2);
502 }
503 }
504 SG_DBGOUT ("Found glade file");
505
506 // connect signal handlers:
507 gtk_builder_connect_signals (xml, NULL);
508
509 //connect the GTK main objects:
510 //Get the main window:
511 main_window = GTK_WIDGET (gtk_builder_get_object (xml, "window_main"));
512 //gtk_widget_set_name (main_window, "Gnaural Binaural Beat Generator");
513 if (main_window == NULL)
514 {
515 SG_DBGOUT ("Didn't fine main_window in glade file");
516 }
517
518 //Get the progress bar:
519 main_ProgressBar =
520 GTK_WIDGET (gtk_builder_get_object (xml, "progressbar_main"));
521 if (main_ProgressBar == NULL)
522 {
523 SG_DBGOUT ("Didn't fine progressbar_main in glade file");
524 }
525
526 // create the drawing area (now create 20100626):
527 main_drawing_area = gtk_drawing_area_new (); //20100626
528 //determine which events it should receive:
529 gtk_widget_add_events (GTK_WIDGET (main_drawing_area),
530 GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK |
531 GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK |
532 GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
533 GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK |
534 GDK_LEAVE_NOTIFY_MASK);
535
536 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (main_drawing_area), GTK_CAN_FOCUS);
537
538 //now setup all the signals:
539 g_signal_connect (main_drawing_area, "realize",
540 G_CALLBACK (on_drawingarea_graph_realize), NULL);
541 g_signal_connect (main_drawing_area, "configure_event",
542 G_CALLBACK (on_drawingarea_graph_configure_event), NULL);
543 g_signal_connect (main_drawing_area, "delete_event",
544 G_CALLBACK (on_drawingarea_graph_delete_event), NULL);
545 g_signal_connect (main_drawing_area, "expose_event",
546 G_CALLBACK (on_drawingarea_graph_expose_event), NULL);
547 g_signal_connect (main_drawing_area, "button_press_event",
548 G_CALLBACK (on_drawingarea_graph_button_press_event),
549 NULL);
550 g_signal_connect (main_drawing_area, "button_release_event",
551 G_CALLBACK (on_drawingarea_graph_button_release_event),
552 NULL);
553 g_signal_connect (main_drawing_area, "key_press_event",
554 G_CALLBACK (on_drawingarea_graph_key_press_event), NULL);
555 g_signal_connect (main_drawing_area, "key_release_event",
556 G_CALLBACK (on_drawingarea_graph_key_release_event), NULL);
557 g_signal_connect (main_drawing_area, "motion_notify_event",
558 G_CALLBACK (on_drawingarea_graph_motion_notify_event),
559 NULL);
560 g_signal_connect (main_drawing_area, "enter_notify_event",
561 G_CALLBACK (on_drawingarea_graph_enter_notify_event),
562 NULL);
563 //Set size to something rational then show it:
564 gtk_widget_set_size_request (GTK_WIDGET (main_drawing_area), 800, 128);
565 gtk_widget_show (main_drawing_area);
566
567 //create the Voices frame:
568 main_frameVoices = gtk_frame_new ("Voices");
569
570 //create the Voices vbox:
571 main_vboxVoices = (GtkVBox *) gtk_vbox_new (FALSE, 0);
572
573 //add the vbox to the frame:
574 gtk_container_add (GTK_CONTAINER (main_frameVoices),
575 GTK_WIDGET (main_vboxVoices));
576
577 main_vpanedListGraph =
578 GTK_WIDGET (gtk_builder_get_object (xml, "vpanedListGraph"));
579 gtk_paned_add1 (GTK_PANED (main_vpanedListGraph), main_frameVoices);
580 gtk_paned_add2 (GTK_PANED (main_vpanedListGraph), main_drawing_area);
581 gtk_widget_show_all (main_vpanedListGraph);
582
583 //get the statusbar:
584 main_Statusbar =
585 (GtkStatusbar *)
586 GTK_WIDGET (gtk_builder_get_object (xml, "statusbar_main"));
587
588 //get the Play/Pause button:
589 main_buttonPlayPause =
590 (GtkButton *) GTK_WIDGET (gtk_builder_get_object (xml, "buttonPlay"));
591
592 //get menuitems that will need updates:
593 main_togglebuttonViewFreq =
594 (GtkToggleButton *) GTK_WIDGET (gtk_builder_get_object (xml,
595 "radiobuttonGraphView_BaseFreq"));
596 main_togglebuttonViewBeat =
597 (GtkToggleButton *) GTK_WIDGET (gtk_builder_get_object (xml,
598 "radiobuttonGraphView_BeatFreq"));
599 main_togglebuttonViewVol =
600 (GtkToggleButton *) GTK_WIDGET (gtk_builder_get_object (xml,
601 "radiobuttonGraphView_Volume"));
602 main_togglebuttonViewBal =
603 (GtkToggleButton *) GTK_WIDGET (gtk_builder_get_object (xml,
604 "radiobuttonGraphView_Balance"));
605
606 //20100528: see if there's a /usr/share/gnaural/preset style library
607 //and set menu item accordingly:
608 GtkWidget *w =
609 GTK_WIDGET (gtk_builder_get_object (xml, "menuitem_OpenFromLibrary"));
610 if (0 != main_TestPathOrFileExistence (GNAURAL_PRESET_DIR))
611 {
612 gtk_widget_set_sensitive (w, FALSE);
613 }
614
615 //get all labels:
616 main_labelFileName =
617 (GtkLabel *) GTK_WIDGET (gtk_builder_get_object (xml, "labelFileName"));
618 main_labelFileAuthor =
619 (GtkLabel *) GTK_WIDGET (gtk_builder_get_object (xml, "labelFileAuthor"));
620 main_labelFileDescription =
621 (GtkLabel *)
622 GTK_WIDGET (gtk_builder_get_object (xml, "labelFileDescription"));
623 main_labelFileTitle =
624 (GtkLabel *) GTK_WIDGET (gtk_builder_get_object (xml, "labelFileTitle"));
625 main_labelStatus =
626 (GtkLabel *) GTK_WIDGET (gtk_builder_get_object (xml, "labelStatus"));
627 main_labelTotalRuntime =
628 (GtkLabel *) GTK_WIDGET (gtk_builder_get_object (xml, "labelTotalRuntime"));
629 main_labelCurrentRuntime =
630 (GtkLabel *)
631 GTK_WIDGET (gtk_builder_get_object (xml, "labelCurrentRuntime"));
632
633 //get Entry:
634 main_entryLoops =
635 (GtkEntry *) GTK_WIDGET (gtk_builder_get_object (xml, "entryLoops"));
636
637 //get the Volume hscale:
638 main_hscaleVolume =
639 (GtkHScale *) GTK_WIDGET (gtk_builder_get_object (xml, "hscaleVolume"));
640
641 //get the Volume hscale:
642 main_hscaleBalance =
643 (GtkHScale *) GTK_WIDGET (gtk_builder_get_object (xml, "hscaleBalance"));
644
645 //get the checkbuttonSwapStereo:
646 main_checkbuttonSwapStereo =
647 (GtkToggleButton *)
648 GTK_WIDGET (gtk_builder_get_object (xml, "checkbuttonSwapStereo"));
649
650 //get the checkbuttonSwapStereo:
651 main_checkbuttonOutputMono =
652 (GtkToggleButton *)
653 GTK_WIDGET (gtk_builder_get_object (xml, "checkbuttonOutputMono"));
654
655 //20101014: do the recent file stuff:
656 gnauralRecentMenu_Init (xml);
657
658 //20101014: setup volume meters
659 gnauralVU_init (xml);
660
661 return 0;
662 }
663
664 /////////////////////////////////////////////////////
665 //20101012: DND is poorly documented and so poorly implemented
666 //that i have to leave this basically a complete hack,
667 //all it really does is allow user to drag a file fron nautilus
668 //Main problem in a nutshell is that DND isn't differentiating between
669 //URIs and Strings for some reason. The best info seems to be here:
670 //http://live.gnome.org/GnomeLove/DragNDropTutorial
671 /////////////////////////////////////////////////////
main_OnDragDataReceived(GtkWidget * widget,GdkDragContext * context,int x,int y,GtkSelectionData * selection_data,guint target_type,guint time,gpointer userdata)672 void main_OnDragDataReceived (GtkWidget * widget,
673 GdkDragContext * context,
674 int x,
675 int y,
676 GtkSelectionData * selection_data,
677 guint target_type, guint time,
678 gpointer userdata)
679 {
680 gboolean dnd_success = FALSE;
681 gboolean delete_selection_data = FALSE;
682
683 if ((selection_data != NULL) && (selection_data->length >= 0))
684 {
685 if (context->action == GDK_ACTION_MOVE)
686 delete_selection_data = TRUE;
687 if (DND_TARGET_STRING == target_type)
688 {
689 dnd_success = TRUE;
690
691 //for some reason, g_filename_from_uri returns lots of garbage,
692 //so just hunt for any valid filename out of whatever came in the
693 //ugliest manner possible:
694 gchar *filename =
695 g_filename_from_uri ((char *) selection_data->data, NULL, NULL);
696
697 if (NULL == filename)
698 {
699 SG_ERROUT ("DND: invalid filename");
700 return; //hack
701 }
702
703 SG_DBGOUT_INT ("DND format:", selection_data->format);
704 SG_DBGOUT_INT ("DND length:", selection_data->length);
705 SG_DBGOUT_STR ("DND data:", selection_data->data);
706
707 //int i = selection_data->length;//OOPS! i'm an idiot!
708 int i = strlen (filename);
709 while (i > 0)
710 {
711 if (0 == main_TestPathOrFileExistence (filename))
712 //if (TRUE == g_file_test (filename, G_FILE_TEST_EXISTS))
713 {
714 break;
715 }
716 --i;
717 filename[i] = '\0';
718 SG_DBGOUT_STR ("DND: invalid file:", filename);
719 SG_DBGOUT_INT ("i:", i);
720 }
721
722 if (i > 0)
723 {
724 SG_DBGOUT_STR ("DND: valid file:", filename);
725 gxml_XMLReadFile (filename, main_drawing_area, FALSE);
726 }
727 else
728 {
729 SG_ERROUT ("DND: invalid filename");
730 }
731 SG_DBGOUT_STR ("freeing filename", filename);
732 g_free (filename); //this causes segfaults sometimes for no apparent reason
733 }
734 else
735 {
736 SG_DBGOUT ("DND failed");
737 }
738 }
739 //this basically avoids ghost icon dragging across screen a min. later
740 gtk_drag_finish (context, dnd_success, delete_selection_data, time);
741 }
742
743 /////////////////////////////////////////////////////
main_ResetScheduleInfo()744 void main_ResetScheduleInfo ()
745 {
746 SG_StringAllocateAndCopy (&main_Info_Author, NULL);
747 SG_StringAllocateAndCopy (&main_Info_Title, NULL);
748 SG_StringAllocateAndCopy (&main_Info_Description, NULL);
749 }
750
751 /////////////////////////////////////////////////////
main_button_release_event(GtkWidget * widget,GdkEventButton * event)752 gboolean main_button_release_event (GtkWidget * widget,
753 GdkEventButton * event)
754 {
755 SG_button_release_event (widget, event);
756 // if (SG_GraphHasChanged == TRUE)
757 if (SG_DataHasChanged == TRUE)
758 {
759 main_LoadBinauralBeatSoundEngine ();
760 }
761 return TRUE;
762 }
763
764 /////////////////////////////////////////////////////
main_motion_notify_event(GtkWidget * widget,GdkEventMotion * event)765 gboolean main_motion_notify_event (GtkWidget * widget, GdkEventMotion * event)
766 {
767 SG_motion_notify_event (widget, event);
768 //SG_GraphHasChanged = TRUE;
769 return FALSE;
770 }
771
772 /////////////////////////////////////////////////////
main_button_press_event(GtkWidget * widget,GdkEventButton * event)773 gboolean main_button_press_event (GtkWidget * widget, GdkEventButton * event)
774 {
775 SG_button_press_event (widget, event);
776 if (SG_DataHasChanged == TRUE)
777 {
778 main_LoadBinauralBeatSoundEngine ();
779 }
780 return TRUE;
781 }
782
783 /////////////////////////////////////////////////////
main_key_release_event(GtkWidget * widget,GdkEventKey * event)784 gboolean main_key_release_event (GtkWidget * widget, GdkEventKey * event)
785 {
786 main_KeyDown = FALSE;
787 return FALSE;
788 }
789
790 /////////////////////////////////////////////////////
main_key_press_event(GtkWidget * widget,GdkEventKey * event)791 gboolean main_key_press_event (GtkWidget * widget, GdkEventKey * event)
792 {
793 //NOTE: MOST GNAURAL KEYBOARD INPUT IS HANDLED BY GTK ACCELLERATORS,
794 //therefore almost everything here gets served from callbacks.c:
795
796 // widget = main_drawing_area;
797 //====deal with keys pressed with Ctrl:
798 if (event->state & GDK_CONTROL_MASK)
799 {
800 switch (event->keyval)
801 {
802 case GDK_a: //select all:
803 SG_SelectDataPoints (-8, -8, widget->allocation.width + 8,
804 widget->allocation.height + 8, TRUE);
805 SG_ConvertDataToXY (widget);
806 SG_DrawGraph (widget);
807 break;
808
809 case GDK_b:
810 { //Scale Time:
811 main_DuplicateSelectedVoice ();
812 }
813 break;
814
815 case GDK_c: //copy:
816 SG_CopySelectedDataPoints (widget);
817 main_UpdateGUI_Progressbar ();
818 break;
819
820 case GDK_d: //Delete currently selected voice:
821 main_DeleteSelectedVoice (widget);
822 break;
823
824 case GDK_e: //Select All Points in currently selected voice:
825 SG_SelectDataPoints_Voice (SG_SelectVoice (NULL), TRUE);
826 SG_DrawGraph (widget);
827 break;
828
829 case GDK_g:
830 main_ScaleDatPoints_Y (widget);
831 break;
832
833 //GDK_h is used by Help
834
835 case GDK_i: //Inverts selections in all visible voices:
836 SG_SelectInvertDataPoints_All ();
837 SG_DrawGraph (widget);
838 break;
839
840 case GDK_j: //add new voice:
841 main_VoicePropertiesDialog (widget, NULL);
842 break;
843
844 case GDK_k: //Select Interval:
845 main_SelectInterval ();
846 break;
847
848 case GDK_l: //line up points:
849 SG_BackupDataPoints (main_drawing_area);
850 SG_AlignDataPoints (widget);
851 break;
852
853 case GDK_m:
854 main_SelectNeighbor ();
855 break;
856
857 //GDK_n is already used
858
859 case GDK_o:
860 main_OpenFile (FALSE, NULL);
861 break;
862
863 case GDK_p: //Apply Graph to sound engine:
864 main_LoadBinauralBeatSoundEngine ();
865 break;
866
867 case GDK_q: //quit:
868 main_quit ();
869 break;
870
871 case GDK_r: //reverse voice:
872 main_ReverseVoice ();
873 break;
874
875 case GDK_s: //save to file:
876 gxml_XMLWriteFile (main_sCurrentGnauralFilenameAndPath);
877 break;
878
879 case GDK_t:
880 { //Edit Voice Properties:
881 SG_Voice *tmpVoice = SG_SelectVoice (NULL);
882
883 if (tmpVoice != NULL)
884 {
885 main_VoicePropertiesDialog (widget, tmpVoice);
886 }
887 }
888 break;
889
890 case GDK_u: //Select last DPs in visible voices:
891 main_SelectLastDPs ();
892 break;
893
894 case GDK_v: //paste:
895 SG_BackupDataPoints (main_drawing_area);
896 SG_PasteSelectedDataPoints (widget, TRUE);
897 break;
898
899 case GDK_w: //Change Graph View:
900 switch (SG_GraphType)
901 {
902 case SG_GRAPHTYPE_BEATFREQ:
903 main_SetGraphType (main_togglebuttonViewVol);
904 break;
905
906 case SG_GRAPHTYPE_BASEFREQ:
907 main_SetGraphType (main_togglebuttonViewBeat);
908 break;
909
910 case SG_GRAPHTYPE_VOLUME:
911 main_SetGraphType (main_togglebuttonViewBal);
912 break;
913
914 case SG_GRAPHTYPE_VOLUME_BALANCE:
915 main_SetGraphType (main_togglebuttonViewFreq);
916 break;
917
918 default:
919 main_SetGraphType (main_togglebuttonViewFreq);
920 break;
921 }
922 SG_ConvertDataToXY (widget);
923 SG_DrawGraph (widget);
924 break;
925
926 case GDK_x: //cut:
927 SG_BackupDataPoints (widget);
928 SG_CopySelectedDataPoints (widget);
929 SG_DeleteDataPoints (widget, FALSE, TRUE);
930 break;
931
932 case GDK_y: //undo-redo:
933 case GDK_z:
934 //NOTE: undo/redo are dealt with by accellerators because their workings are closely tied with menu states:
935 BB_ResetAllVoices (); //a20070730
936 SG_RestoreDataPoints (widget, FALSE);
937 break;
938 } //end keyval
939 return FALSE;
940 } // end deal with keys pressed with Ctrl
941
942 //====deal with keys pressed with Shift:
943 if (event->state & GDK_SHIFT_MASK)
944 {
945 switch (event->keyval)
946 {
947 case GDK_A: //deselect all:
948 SG_DeselectDataPoints ();
949 SG_DrawGraph (widget);
950 break;
951
952 case GDK_E: //Deselect All Points in currently selected voice:
953 SG_SelectDataPoints_Voice (SG_SelectVoice (NULL), FALSE);
954 SG_DrawGraph (widget);
955 break;
956
957 case GDK_I: //Inverts selections in all visible voices:
958 SG_SelectInvertDataPoints_Voice (SG_SelectVoice (NULL));
959 SG_DrawGraph (widget);
960 break;
961
962 case GDK_U: //Select first DPs in visible voices:
963 main_SelectFirstDPs ();
964 break;
965
966 case GDK_V: //Pastes whatever's in buffer to very end of schedule
967 main_PasteAtEnd ();
968 break;
969
970 case GDK_X: //cut (deleting time also):
971 SG_BackupDataPoints (widget);
972 SG_CopySelectedDataPoints (widget);
973 SG_DeleteDataPoints (widget, TRUE, TRUE);
974 break;
975
976 case GDK_Delete:
977 //delete points and delete points and time (Shift):
978 SG_BackupDataPoints (widget);
979 SG_DeleteDataPoints (widget, (event->state & GDK_SHIFT_MASK), TRUE);
980 break;
981
982 //a20070620 :
983 case GDK_Up:
984 case GDK_Down:
985 main_key_arrowkeyhandler (widget, (event->keyval == GDK_Up) ? -10 : 10, 0);
986 break;
987
988 //a20070620 :
989 case GDK_Left:
990 case GDK_Right:
991 main_key_arrowkeyhandler (widget, 0,
992 (event->keyval == GDK_Left) ? -10 : 10);
993 break;
994 } //end keyval
995 return FALSE;
996 } // end deal with keys pressed with Shift
997
998 //====Finally, deal with pure keys:
999 switch (event->keyval)
1000 {
1001 case GDK_Delete:
1002 SG_BackupDataPoints (widget);
1003 SG_DeleteDataPoints (widget, (event->state & GDK_SHIFT_MASK), TRUE);
1004 break;
1005
1006 //a20070620 :
1007 case GDK_Up:
1008 case GDK_Down:
1009 main_key_arrowkeyhandler (widget, (event->keyval == GDK_Up) ? -1 : 1, 0);
1010 break;
1011
1012 //a20070620 :
1013 case GDK_Left:
1014 case GDK_Right:
1015 main_key_arrowkeyhandler (widget, 0, (event->keyval == GDK_Left) ? -1 : 1);
1016 break;
1017 } //end keyval
1018 return FALSE;
1019 } //end all keys
1020
1021 /////////////////////////////////////////////////////
1022 //a20070620:
main_key_arrowkeyhandler(GtkWidget * widget,int vertical,int horizontal)1023 void main_key_arrowkeyhandler (GtkWidget * widget,
1024 int vertical, int horizontal)
1025 {
1026 if (vertical == 0 && horizontal == 0)
1027 {
1028 return;
1029 }
1030 //20070620 now deal with backup:
1031 if (main_KeyDown == FALSE)
1032 {
1033 SG_BackupDataPoints (widget); //a20070620
1034 }
1035 main_KeyDown = TRUE;
1036 if (vertical != 0)
1037 {
1038 SG_MoveSelectedDataPoints (widget, 0, vertical);
1039 SG_ConvertYToData_SelectedPoints (widget);
1040 }
1041
1042 //a20070620 :
1043 if (horizontal != 0)
1044 {
1045 SG_MoveSelectedDataPoints (widget, horizontal, 0);
1046 SG_ConvertXToDuration_AllPoints (widget);
1047 }
1048
1049 SG_ConvertDataToXY (widget); // NOTE: I need to call this both to set limits and to bring XY's outside of graph back in
1050 SG_DrawGraph (widget);
1051 SG_DataHasChanged = SG_GraphHasChanged = TRUE; //20070105 tricky way to do main_LoadBinauralBeatSoundEngine in a bulk way
1052 }
1053
1054 /////////////////////////////////////////////////////
1055 // Initializing or resizing, so create a new backing pixmap of the appropriate size
main_configure_event(GtkWidget * widget,GdkEventConfigure * event)1056 gboolean main_configure_event (GtkWidget * widget, GdkEventConfigure * event)
1057 {
1058 if (main_pixmap_Graph != NULL)
1059 {
1060 SG_DBGOUT ("Destroying old pixmap");
1061 g_object_unref (main_pixmap_Graph);
1062 }
1063
1064 SG_DBGOUT ("Creating new pixmap");
1065 main_pixmap_Graph =
1066 gdk_pixmap_new (widget->window, widget->allocation.width,
1067 widget->allocation.height, -1);
1068 // init the graph:
1069 SG_DBGOUT ("Initing SG_Graph");
1070 SG_Init (main_pixmap_Graph);
1071
1072 //try commenting these out:
1073 SG_ConvertDataToXY (widget);
1074 SG_DrawGraph (widget);
1075 return TRUE;
1076 }
1077
1078 /////////////////////////////////////////////////////
1079 // This is the repaint signal, so redraw the screen from the backing pixmap
main_expose_event(GtkWidget * widget,GdkEventExpose * event)1080 gboolean main_expose_event (GtkWidget * widget, GdkEventExpose * event)
1081 {
1082 /*
1083 The GtkDrawingArea widget is used for creating custom user interface elements.
1084 It's essentially a blank widget; you can draw on widget->window. After creating a
1085 drawing area, the application may want to connect to:
1086
1087 - Mouse and button press signals to respond to input from the user.
1088 (Use gtk_widget_add_events() to enable events you wish to receive.)
1089
1090 - The "realize" signal to take any necessary actions when the widget is instantiated
1091 on a particular display. (Create GDK resources in response to this signal.)
1092
1093 - The "SG_configure_event" signal to take any necessary actions when the widget changes size.
1094
1095 - The "expose_event" signal to handle redrawing the contents of the widget.
1096
1097 GDK automatically clears the exposed area to the background color before sending the
1098 expose event, and that drawing is implicitly clipped to the exposed area.
1099 */
1100 gdk_draw_drawable (widget->window,
1101 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
1102 main_pixmap_Graph, event->area.x, event->area.y,
1103 event->area.x, event->area.y, event->area.width,
1104 event->area.height);
1105
1106 return FALSE;
1107 }
1108
1109 /////////////////////////////////////////////////////
1110 //NOTE: there is also a main_Cleanup(); not sure if they are in all cases cooperative.
main_quit()1111 void main_quit ()
1112 {
1113 // exit (0);
1114 g_source_remove (GUIUpdateHandle);
1115 GUIUpdateHandle = 0;
1116
1117 SG_Cleanup ();
1118 gtk_main_quit ();
1119 }
1120
1121 /////////////////////////////////////////////////////
1122 //20070702: This apparently never actually gets called:
1123 //"problem is the common one where libglade has already
1124 //shown the widget before the signal handlers are connected, so you never
1125 //get the signal. The solution is to set the window's 'Visible' property
1126 //to FALSE in glade, and call gtk_widget_show() on it after calling
1127 //glade_xml_signal_autoconnect()."
main_realize(GtkWidget * widget,GdkEventConfigure * event)1128 gboolean main_realize (GtkWidget * widget, GdkEventConfigure * event)
1129 {
1130 return FALSE;
1131 }
1132
1133 /////////////////////////////////////////////////////
main_UpdateGUI_entryLoops()1134 void main_UpdateGUI_entryLoops ()
1135 {
1136 sprintf (main_sTmp, "%d", BB_Loops);
1137 gtk_entry_set_text (GTK_ENTRY (main_entryLoops), main_sTmp);
1138 }
1139
1140 /////////////////////////////////////////////////////
main_SetLoops(unsigned int loops)1141 void main_SetLoops (unsigned int loops)
1142 {
1143 int diff = BB_Loops - BB_LoopCount;
1144
1145 if (loops != 0)
1146 { //i.e., we're not in inf. loop mode:
1147 if (diff >= loops)
1148 BB_LoopCount = 1; //this keeps it from running forever
1149 else
1150 BB_LoopCount = loops - diff;
1151 }
1152 else
1153 { // inf. loop mode:
1154 BB_LoopCount = 0;
1155 }
1156 BB_Loops = loops;
1157 main_UpdateGUI_entryLoops ();
1158 main_UpdateGUI_ProjectedRuntime ();
1159 // main_LoadBinauralBeatSoundEngine();
1160 }
1161
1162 /////////////////////////////////////////////////////
main_on_entryLoops_activate(GtkEntry * entry,gpointer user_data)1163 void main_on_entryLoops_activate (GtkEntry * entry, gpointer user_data)
1164 {
1165 // const gchar *entry_text;
1166 // entry_text = gtk_entry_get_text (GTK_ENTRY (entry));
1167 // printf ("Entry contents: %s\n", entry_text);
1168 // const gchar *entry_text = gtk_entry_get_text (GTK_ENTRY (entry));
1169 main_SetLoops (((unsigned
1170 int) (g_ascii_strtod (gtk_entry_get_text
1171 (GTK_ENTRY (entry)), NULL))));
1172 }
1173
1174 /////////////////////////////////////////////////////
1175 //Takes a pointers to hold the Audio Data and a filename, and checks
1176 //if it is valid data, loads it in to a local list so that BB can
1177 //just grab pointers to already loaded audio data here instead of having
1178 //to reload entire audio files every reload.
main_ProcessAudioFile(char * filename,int ** buffer,unsigned int * size)1179 void main_ProcessAudioFile (char *filename, int **buffer, unsigned int *size)
1180 {
1181 int i;
1182
1183 for (i = 0; i < main_AudioFileData_count; i++)
1184 {
1185 if (0 == strcmp (filename, main_AudioFileData[i].filename))
1186 { //file was already opened:
1187 (*buffer) = main_AudioFileData[i].PCM_data;
1188 (*size) = main_AudioFileData[i].PCM_data_size;
1189 SG_DBGOUT_STR ("Audio Data already loaded, not loading", filename);
1190 return;
1191 }
1192 }
1193 SG_DBGOUT_STR ("Audio Data not already loaded, loading", filename);
1194
1195 //i could try this, but why add an extra step?:
1196 //int main_TestPathOrFileExistence (char *PathName)
1197
1198 //If i got here, i didn't already load the audio file, so first need
1199 //to try to open the explicit filename:
1200 if (0 != main_LoadSoundFile (filename, &(*buffer), &(*size)))
1201 { //that failed, so now assume filename was using a relative path:
1202 SG_DBGOUT_STR ("No valid audio file:", filename);
1203 SG_DBGOUT ("Trying schedule file's directory...");
1204 //20100628: adding code to look for unpathed filenames in the current
1205 //schedule-file's directory:
1206 gchar *tmpfile = g_path_get_dirname (main_sCurrentGnauralFilenameAndPath);
1207 gchar tmpstr[PATH_MAX];
1208 sprintf (tmpstr, "%s/%s", tmpfile, filename);
1209 g_free (tmpfile);
1210 if (0 != main_LoadSoundFile (tmpstr, &(*buffer), &(*size)))
1211 { //totally failed, giving up (could try ~/.gnaural, but am not):
1212 SG_ERROUT ("No valid audio file:");
1213 SG_ERROUT (tmpstr);
1214 return;
1215 }
1216 }
1217 //successful load of PCM data, now need to add it to a list.
1218 //first make a copy of the list, one-larger than original:
1219 main_AFData *tmp_AudioFileData =
1220 (main_AFData *) malloc (sizeof (main_AFData) *
1221 (main_AudioFileData_count + 1));
1222 if (NULL != main_AudioFileData)
1223 {
1224 memcpy (tmp_AudioFileData, main_AudioFileData,
1225 sizeof (main_AFData) * main_AudioFileData_count);
1226 }
1227 SG_DBGOUT_INT ("Number of audio files open:", main_AudioFileData_count + 1);
1228
1229 //now add new last element's data:
1230 tmp_AudioFileData[main_AudioFileData_count].PCM_data = (*buffer);
1231 tmp_AudioFileData[main_AudioFileData_count].PCM_data_size = (*size);
1232 tmp_AudioFileData[main_AudioFileData_count].filename = NULL; //IMPORTANT!
1233 SG_StringAllocateAndCopy (&
1234 (tmp_AudioFileData
1235 [main_AudioFileData_count].filename), filename);
1236 if (NULL != main_AudioFileData)
1237 {
1238 free (main_AudioFileData);
1239 }
1240 main_AudioFileData = tmp_AudioFileData;
1241 ++main_AudioFileData_count;
1242 }
1243
1244 /////////////////////////////////////////////////////
1245 //main_LoadBinauralBeatSoundEngine() Loads BB with
1246 //whatever data is currently in SG data.
1247 //Call this whenever ScheduleGUI and BinauralBeat aren't
1248 //data sync'd
main_LoadBinauralBeatSoundEngine()1249 void main_LoadBinauralBeatSoundEngine ()
1250 {
1251 GN_Time_ResetSeniority ();
1252 SG_DBGOUT ("Copying Graph to BB Engine");
1253 SG_Voice *curVoice;
1254
1255 SG_DataPoint *curDP;
1256
1257 int count_voice;
1258
1259 int count_dp;
1260
1261 // first count the number of voices:
1262 count_voice = 0;
1263 curVoice = SG_FirstVoice;
1264 while (curVoice != NULL)
1265 {
1266 ++count_voice;
1267 curVoice = curVoice->NextVoice;
1268 }
1269
1270 SG_DBGOUT_INT ("Total number of Voices:", count_voice);
1271
1272 //1) prepare the sound engine for that many voices:
1273 //NOTE: as of 20070731, this also cleans old BB_Voice data:
1274 BB_InitVoices (count_voice);
1275
1276 SG_DBGOUT_INT ("Preparing sound engine, number of voices:", count_voice);
1277 //now go through each voice, count the datapoints,
1278 //put the datapoints in a raw array, then feed it to
1279 //the sound engine's data stuctures:
1280 count_voice = 0;
1281 curVoice = SG_FirstVoice;
1282
1283 //CRUCIAL to zero BB_TotalDuration, or else old value might persist (see BB Notes section):
1284 BB_TotalDuration = 0;
1285 while (curVoice != NULL)
1286 {
1287 //first count how many DPs this voice has:
1288 count_dp = 0;
1289 curDP = curVoice->FirstDataPoint;
1290 do
1291 {
1292 ++count_dp;
1293 //all list loops need this:
1294 curDP = curDP->NextDataPoint;
1295 }
1296 while (curDP != NULL);
1297 /*
1298 SG_DBGOUT_INT ("Voice:", count_voice);
1299 SG_DBGOUT_INT ("\tType:", curVoice->type);
1300 SG_DBGOUT_INT ("\tEvent Count:", count_dp);
1301 */
1302 //2) Allot Entry memory for each voice by running BB_SetupVoice() on each:
1303 BB_SetupVoice (count_voice, curVoice->type, curVoice->mute, curVoice->mono,
1304 count_dp);
1305
1306 //3) Load memory for each entry alotted in step 2:
1307 count_dp = 0;
1308 curDP = curVoice->FirstDataPoint;
1309 do
1310 {
1311 BB_Voice[count_voice].Entry[count_dp].beatfreq_start_HALF =
1312 curDP->beatfreq * 0.5;
1313 /*
1314 BB_Voice[count_voice].Entry[count_dp].beatfreqL_start =
1315 curDP->beatfreq * 0.5;
1316 BB_Voice[count_voice].Entry[count_dp].beatfreqR_start =
1317 curDP->beatfreq * -0.5;
1318 */
1319 BB_Voice[count_voice].Entry[count_dp].duration = curDP->duration;
1320 BB_Voice[count_voice].Entry[count_dp].basefreq_start = curDP->basefreq;
1321 BB_Voice[count_voice].Entry[count_dp].volL_start = curDP->volume_left;
1322 BB_Voice[count_voice].Entry[count_dp].volR_start = curDP->volume_right;
1323 ++count_dp;
1324 //all list loops need this:
1325 curDP = curDP->NextDataPoint;
1326 }
1327 while (curDP != NULL);
1328 /*
1329 SG_DBGOUT_INT("Voice Type:", BB_Voice[count_voice].type);
1330 SG_DBGOUT_FLT("Voice Entry HZ:", BB_Voice[count_voice].Entry[2].beatfreqL_start);
1331 SG_DBGOUT_FLT("Voice Entry Duration:", BB_Voice[count_voice].Entry[2].duration);
1332 SG_DBGOUT_FLT("Voice Entry basefreq:", BB_Voice[count_voice].Entry[2].basefreq_start);
1333 SG_DBGOUT_FLT("Voice Entry volume:", BB_Voice[count_voice].Entry[2].volL_start);
1334 */
1335
1336 //4) Run BB_CalibrateVoice() on the voice:
1337 BB_CalibrateVoice (count_voice);
1338
1339 //5) see if this was a PCM voice:
1340 if (BB_VOICETYPE_PCM == curVoice->type)
1341 {
1342 /*
1343 //this works well, but I don't want to load directly in to BB anymore (too inefficient)
1344 main_LoadSoundFile (curVoice->description,
1345 (int **) &(BB_Voice[count_voice].PCM_samples),
1346 &(BB_Voice[count_voice].PCM_samples_samples_size));
1347 */
1348 //This loads locally, allowing me to check if voice is already and then simply assign pointers to BB:
1349 main_ProcessAudioFile (curVoice->description,
1350 (int **) &(BB_Voice[count_voice].PCM_samples),
1351 &(BB_Voice[count_voice].PCM_samples_size));
1352 }
1353
1354 //advance to next voice:
1355 curVoice = curVoice->NextVoice;
1356 ++count_voice;
1357 }
1358
1359 BB_PauseFlag = FALSE; //CRITICAL, added 20070803
1360 //next two lines added 20070129 to deal with weird BB_TotalDuration inconsistencies
1361 //BB_DetermineTotalDuration();
1362 int fixes = BB_FixVoiceDurations ();
1363
1364 SG_DBGOUT_INT ("\tVoices fixed:", fixes);
1365
1366 main_UpdateGUI_ProjectedRuntime ();
1367 }
1368
1369 ///////////////////////////////////////////////////////////////
1370 //Parse user's string for patern/name pairs. Titles are bounded
1371 //by "~" symbols, and subsequent filters trailed by commas, like:
1372 //"~All Files~*,~Audio Files~*.wav,*.aiff,*.au,*.flac,~Image Files~*.jpg,*.gif,*.bmp"
main_DialogAddFileFilters(GtkWidget * dialog,gchar * strFilterString)1373 void main_DialogAddFileFilters (GtkWidget * dialog, gchar * strFilterString)
1374 {
1375 GtkFileFilter *filter;
1376
1377 char *strFilterString_copy = NULL;
1378
1379 //NOTE: Need to work with our own copy of strUserfilter:
1380 SG_StringAllocateAndCopy (&strFilterString_copy, strFilterString);
1381 //Need strFilterString_copy for free'ing later:
1382 char *sFilterString = strFilterString_copy;
1383
1384 char *substr;
1385
1386 BB_DBGOUT_STR ("Filter string: ", sFilterString);
1387 while (NULL != sFilterString && '\0' != *sFilterString)
1388 {
1389 BB_DBGOUT ("Looking for a new title");
1390 //get a title:
1391 while ('~' == *sFilterString && '\0' != *sFilterString)
1392 {
1393 BB_DBGOUT ("Found start of a new title");
1394 substr = sFilterString + 1;
1395 //look for next "~":
1396 while ('~' != *(++sFilterString));
1397 //cap it at temporary "end" for substr:
1398 *sFilterString = '\0';
1399 BB_DBGOUT_STR (" Filter title :", substr);
1400 filter = gtk_file_filter_new ();
1401 gtk_file_filter_set_name (filter, substr);
1402 //now start adding filters for that title:
1403 while ('\0' != *(++sFilterString) && '~' != *sFilterString)
1404 {
1405 substr = sFilterString;
1406 //run to next comma:
1407 while (',' != *sFilterString && '\0' != *sFilterString)
1408 ++sFilterString;
1409 if ('\0' == *sFilterString)
1410 {
1411 BB_DBGOUT_STR ("[last one] Filter pattern:", substr);
1412 gtk_file_filter_add_pattern (filter, substr);
1413 break;
1414 }
1415 //cap for substring
1416 *sFilterString = '\0';
1417 BB_DBGOUT_STR (" Filter pattern:", substr);
1418 gtk_file_filter_add_pattern (filter, substr);
1419 }
1420 BB_DBGOUT ("Done collecting patterns for that title");
1421 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1422 }
1423 }
1424 BB_DBGOUT ("Done parsing user string");
1425 free (strFilterString_copy);
1426 }
1427
1428 //////////////////////////////////////////////////////////
1429 //NOTE: I could be nice and actually have this allocate a string
1430 //that is ensured big enough to hold all sndfile formats,
1431 //but at this juncture this is good enough.
1432 //This function checks sndfile for all usable formats and
1433 //concatenates them in to a formatted string listable by GTK
1434 //dialog comboboxes:
main_MakeAudioFormatsFileFilterString(gchar * strUserfilter,int size)1435 void main_MakeAudioFormatsFileFilterString (gchar * strUserfilter, int size)
1436 {
1437 //start filter string here:
1438 g_strlcpy (strUserfilter, "~Gnaural Audio Files~", size);
1439
1440 //START standard sndfile list code vars=====================
1441 SF_FORMAT_INFO info; //carries format, samplerate, channels, etc.
1442 SF_INFO sfinfo; //carries format, name, and extension
1443 char buffer[128];
1444 int format,
1445 major_count,
1446 subtype_count,
1447 m,
1448 s;
1449
1450 sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int));
1451 sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count,
1452 sizeof (int));
1453 //END standard sndfile list code vars=====================
1454
1455 //START standard sndfile list code=====================
1456 //clean everything up before filling it
1457 memset (&sfinfo, 0, sizeof (sfinfo));
1458 buffer[0] = 0;
1459 sfinfo.channels = 2;
1460
1461 for (m = 0; m < major_count; m++)
1462 {
1463 info.format = m;
1464 sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info));
1465 format = info.format;
1466 for (s = 0; s < subtype_count; s++)
1467 {
1468 info.format = s;
1469 sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &info, sizeof (info));
1470
1471 format = (format & SF_FORMAT_TYPEMASK) | info.format;
1472
1473 sfinfo.format = format;
1474 //--------put my stuff here:
1475
1476 //now check all formats:
1477 //SG_DBGOUT_STR ("Found format:", info.name);
1478 //we're only interested in 16-bit PCM or OGG:
1479 if (main_CheckSndfileFormat (sfinfo.format))
1480 {
1481 info.format = m;
1482 sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info));
1483 g_strlcat (strUserfilter, "*.", size);
1484 g_strlcat (strUserfilter, info.extension, size);
1485 g_strlcat (strUserfilter, ",", size);
1486 BB_DBGOUT_STR ("Adding extension to filter string:", strUserfilter);
1487 }
1488
1489 //--------end my stuff
1490 };
1491 };
1492 //END standard sndfile list code=====================
1493
1494 //end filter string here:
1495 g_strlcat (strUserfilter, "~All Files~*", size);
1496 }
1497
1498 /////////////////////////////////////////////////////
1499 //returns NULL if failed; otherwise, be sure to free
1500 //what this returns with g_free();
1501 //strUserfilter should be formatted like:
1502 //"~Audio Files~*.wav,*.aiff,*.au,*.flac,~Image Files~*.jpg,*.gif,*.bmp"
main_OpenFileDialog(gchar * strUserfilter,char * path)1503 gchar *main_OpenFileDialog (gchar * strUserfilter, char *path)
1504 {
1505 GtkWidget *dialog;
1506
1507 char *filename = NULL;
1508
1509 dialog =
1510 gtk_file_chooser_dialog_new ("Open File", NULL,
1511 GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
1512 GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN,
1513 GTK_RESPONSE_ACCEPT, NULL);
1514
1515 //this isn't very user friendly; it is just easy for me:
1516 //gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1517 // main_sPathGnaural);
1518
1519 //20100528:
1520 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), path);
1521
1522 //these are alternatives:
1523 // gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), main_sCurrentGnauralFilenameAndPath);
1524 // gchar * tmpname = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER (dialog));
1525 // gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), tmpname);
1526 // g_free (tmpname);
1527
1528 main_DialogAddFileFilters (dialog, strUserfilter);
1529
1530 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
1531 {
1532 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1533 }
1534 gtk_widget_destroy (dialog);
1535 return filename;
1536 }
1537
1538 //////////////////////
1539 //Opens a Save As File dialog, fills a user alotted char * array (big enough
1540 //to handle anything) with the filename IF something valid was chosen;
1541 //but be sure to check return value before using it: ==0 means success
1542 //(i.e., use the string), !=0 failure.
main_AskForSaveAsFilename(char * userfilename)1543 int main_AskForSaveAsFilename (char *userfilename)
1544 {
1545 GtkWidget *dialog;
1546
1547 dialog =
1548 gtk_file_chooser_dialog_new ("Save Gnaural Schedule as...", NULL,
1549 GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
1550 GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE,
1551 GTK_RESPONSE_ACCEPT, NULL);
1552 //20070301 This required GTK+2.0 > 2.8, so dropping it for *ugly* stat() hacks.
1553 //NOTE: next call wasn't even available in Debian unstable a year ago; seems there now
1554 // gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
1555 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog),
1556 main_sCurrentGnauralFilenameAndPath);
1557
1558 main_DialogAddFileFilters (dialog, GNAURAL_FILEFILTERSTRING);
1559
1560 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
1561 {
1562 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1563
1564 if (strlen (filename) > 0)
1565 {
1566 strcpy (userfilename, filename);
1567 }
1568 strcpy (userfilename, filename);
1569 g_free (filename);
1570 gtk_widget_destroy (dialog);
1571 return 0; //user picked a filename
1572 }
1573 gtk_widget_destroy (dialog);
1574 return 1; //failed to select a file
1575 }
1576
1577 //======================================
1578 //calls a dialog to get a filename, if file exists, asks if
1579 //user is sure about overwriting; if answer is no, repeats,
1580 //otherwise just overwrites.
main_OnUserSaveAsFile()1581 void main_OnUserSaveAsFile ()
1582 {
1583 int repeat;
1584
1585 do
1586 {
1587 repeat = 0;
1588 //first ask for filename:
1589 if (0 != main_AskForSaveAsFilename (main_sPathTemp))
1590 {
1591 return; //do nothing
1592 }
1593 if (0 == main_TestPathOrFileExistence (main_sPathTemp))
1594 {
1595 if (0 ==
1596 main_MessageDialogBox
1597 ("That file exists. Are you sure you want to overwrite it?",
1598 GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO))
1599 {
1600 ++repeat;
1601 }
1602 }
1603 }
1604 while (0 != repeat);
1605
1606 if (strlen (main_sPathTemp) > 0)
1607 {
1608 gxml_XMLWriteFile (main_sPathTemp);
1609 main_UpdateGUI_FileInfo (main_sPathTemp);
1610 }
1611 }
1612
1613 //========================================
main_on_edit_activate()1614 void main_on_edit_activate ()
1615 { //this is a hack -- no time to write editor or figure out how to access default editor, so let a typical platform editors do it:
1616 //###############FOR LINUX################
1617 #ifndef GNAURAL_WIN32
1618 //NOTE: I think it is trying to launch this twice, maybe a mouse-click artifact? Might want to implement only one instance code
1619 //BUG 20060116: if user leaves Gedit open after exiting, it is as if the sound system is still being owned by Gnaual until Gedit is closed
1620 sprintf (main_sPathTemp, "%s%s%s", "gedit ",
1621 main_sCurrentGnauralFilenameAndPath, " &");
1622 //[20100405: added check to avoid compiler complaints]:
1623 if (0 == system (main_sPathTemp))
1624 {
1625 }
1626 #endif
1627
1628 //###############FOR WIN32################
1629 #ifdef GNAURAL_WIN32
1630 ShellExecute (0, "open", // Operation to perform
1631 //"c:\\windows\\notepad.exe", // Application name
1632 "notepad.exe", // Application name
1633 main_sCurrentGnauralFilenameAndPath, // Additional parameters
1634 0, // Default directory
1635 SW_SHOW);
1636 #endif
1637 }
1638
1639 /////////////////////////////////////////////////////
main_EventToKeypress(guint state,guint keyval)1640 void main_EventToKeypress (guint state, guint keyval)
1641 {
1642 GdkEventKey event;
1643
1644 //in main_key_press_event, only state and keyval are required:
1645 event.state = state;
1646 event.keyval = keyval;
1647 main_key_press_event (main_drawing_area, &event);
1648 }
1649
1650 /////////////////////////////////////////////////////
1651 //this function only exists to support main_AboutDialogBox below
1652 //#include <gnome.h>
main_activate_url(GtkAboutDialog * about,const gchar * uri,gpointer data)1653 void main_activate_url (GtkAboutDialog * about,
1654 const gchar * uri, gpointer data)
1655 {
1656 #ifdef GNAURAL_WIN32
1657 //##########FOR WIN32##########
1658 ShellExecute (NULL, "open", uri, NULL, NULL, SW_SHOWNORMAL);
1659 #else
1660 const gchar *argv[3];
1661 argv[0] = "xdg-open";
1662 argv[1] = uri;
1663 argv[2] = NULL;
1664 if (!g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
1665 NULL, NULL, NULL, NULL))
1666 {
1667 argv[0] = "firefox";
1668 if (!g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
1669 NULL, NULL, NULL, NULL))
1670 {
1671 argv[0] = "mozilla";
1672 if (!g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
1673 NULL, NULL, NULL))
1674 {
1675 argv[0] = "safari";
1676 if (!g_spawn_async
1677 (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL,
1678 NULL))
1679 {
1680 argv[0] = "opera";
1681 if (!g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
1682 NULL, NULL, NULL, NULL))
1683 {
1684 argv[0] = "konqueror";
1685 if (!g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
1686 NULL, NULL, NULL, NULL))
1687 {
1688 argv[0] = "netscape";
1689 g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
1690 NULL, NULL, NULL, NULL);
1691 }
1692 }
1693 }
1694 }
1695 }
1696 }
1697 #endif
1698
1699 }
1700
1701 /////////////////////////////////////////////////////
main_AboutDialogBox()1702 void main_AboutDialogBox ()
1703 {
1704 static GtkWidget *about = NULL;
1705
1706 const gchar *authors[] = {
1707 "Bret Logan <gnaural@users.sourceforge.net>",
1708 NULL
1709 };
1710 const gchar *documenters[] = {
1711 "Bret Logan <gnaural@users.sourceforge.net>",
1712 NULL
1713 };
1714 const gchar *copyright = "Copyright \xc2\xa9 2003-2011 Bret Logan\n";
1715
1716 const gchar *comments =
1717 "A programmable audio generator intended as an aural aid to meditation and relaxation, implementing the binaural beat principle as described in Gerald Oster's Oct. 1973 Scientific American article \"Auditory Beats in the Brain.\"";
1718 const gchar *license =
1719 "This program is free software; you can redistribute it and/or modify\n\
1720 it under the terms of the GNU General Public License as published by\n\
1721 the Free Software Foundation; either version 2 of the License, or\n\
1722 (at your option) any later version.\n\n\
1723 This program is distributed in the hope that it will be useful,\n\
1724 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
1725 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more\n\
1726 details on the GNU General Public License, contact:\n\n\
1727 \tFree Software Foundation, Inc.\n\
1728 \t59 Temple Place - Suite 330\n\
1729 \tBoston, MA 02111-1307, USA.";
1730
1731 const gchar *website = "http://gnaural.sourceforge.net/";
1732
1733 //~ Translators: This is a special message that shouldn't be translated
1734 //~ literally. It is used in the about box to give credits to
1735 //~ the translators.
1736 //~ Thus, you should translate it to your name and email address.
1737 //~ You can also include other translators who have contributed to
1738 //~ this translation; in that case, please write them on separate
1739 //~ lines seperated by newlines (\n).
1740 const gchar *translator_credits = "translator-credits";
1741
1742 if (about != NULL)
1743 {
1744 gtk_window_set_transient_for (GTK_WINDOW (about), GTK_WINDOW (main_window));
1745 gtk_window_present (GTK_WINDOW (about));
1746 return;
1747 }
1748 gtk_about_dialog_set_url_hook (main_activate_url, NULL, NULL);
1749 about = GTK_WIDGET (g_object_new (GTK_TYPE_ABOUT_DIALOG, //
1750 "program-name", "Gnaural", //
1751 "version", VERSION, //
1752 "copyright", copyright, //
1753 "comments", comments, //
1754 "website", website, //
1755 "authors", authors, //
1756 "documenters", documenters, //
1757 "translator-credits", translator_credits, //
1758 "logo", NULL, //this makes it use the default icon set up in main()
1759 "license", license, //
1760 //"wrap-license", FALSE, //
1761 NULL));
1762 gtk_window_set_destroy_with_parent (GTK_WINDOW (about), TRUE);
1763 g_signal_connect (about, "response", G_CALLBACK (gtk_widget_destroy), NULL);
1764 g_signal_connect (about, "destroy", G_CALLBACK (gtk_widget_destroyed),
1765 &about);
1766 gtk_window_set_transient_for (GTK_WINDOW (about), GTK_WINDOW (main_window));
1767 gtk_window_present (GTK_WINDOW (about));
1768 }
1769
1770 /////////////////////////////////////////////////////
1771 //this gets called every GNAURAL_GUI_UPDATEINTERVAL milliseconds
1772 //20070710: changed to only update one GUI feature per call
main_UpdateGUI(gpointer data)1773 gboolean main_UpdateGUI (gpointer data)
1774 {
1775 static int curcount = 0;
1776
1777 if (++curcount > 3)
1778 {
1779 curcount = 0;
1780 }
1781
1782 //added 20071126:
1783 gdk_threads_enter ();
1784
1785 //deal with Y slider:
1786 if (TRUE == main_vscale_Y_mousebuttondown)
1787 {
1788 main_slider_XY_handler (main_vscale_Y, 0);
1789 }
1790 else if (TRUE == main_hscale_X_mousebuttondown)
1791 {
1792 main_slider_XY_handler (0, main_hscale_X);
1793 }
1794
1795 switch (curcount)
1796 {
1797 case 0:
1798 //added 20070105 to deal with so many inputs not being updated:
1799 if (TRUE == SG_DataHasChanged)
1800 {
1801 main_LoadBinauralBeatSoundEngine ();
1802 SG_DataHasChanged = FALSE;
1803 VG_RelistFlag = TRUE;
1804 }
1805 break;
1806
1807 case 1:
1808 main_UpdateGUI_PlaceInGraph ();
1809 break;
1810
1811 case 2:
1812 main_UpdateGUI_Progressbar ();
1813 if (TRUE == SG_GraphHasChanged)
1814 {
1815 main_UpdateGUI_Voices (main_vboxVoices);
1816 SG_GraphHasChanged = FALSE;
1817 }
1818 break;
1819
1820 case 3:
1821 main_UpdateGUI_Labels ();
1822 main_UpdateVU ();
1823 break;
1824 }
1825
1826 //added 20071126:
1827 gdk_threads_leave ();
1828
1829 return TRUE;
1830 }
1831
1832 /////////////////////////////////////////////////////
main_UpdateGUI_PlaceInGraph()1833 void main_UpdateGUI_PlaceInGraph ()
1834 {
1835 SG_DrawGraph (main_drawing_area);
1836 }
1837
1838 /////////////////////////////////////////////////////
main_UpdateGUI_Progressbar()1839 void main_UpdateGUI_Progressbar ()
1840 {
1841 long total =
1842 (BB_CurrentSampleCountLooped +
1843 BB_CurrentSampleCount) / (int) BB_AUDIOSAMPLERATE;
1844 float i = 0;
1845
1846 if (BB_TotalDuration > 0)
1847 {
1848 if (BB_Loops > 0)
1849 {
1850 i = total / (float) (BB_TotalDuration * BB_Loops);
1851 }
1852 if (i > 1)
1853 {
1854 i = 1;
1855 }
1856 else if (i < 0)
1857 {
1858 i = 0;
1859 }
1860 gtk_progress_bar_set_fraction ((GtkProgressBar *) main_ProgressBar, i);
1861 }
1862 else
1863 {
1864 gtk_progress_bar_set_fraction ((GtkProgressBar *) main_ProgressBar, 0);
1865 }
1866 }
1867
1868 //======================================
main_OnButton_Play(GtkButton * button)1869 void main_OnButton_Play (GtkButton * button)
1870 {
1871 GN_Time_ResetSeniority ();
1872 if (main_AudioWriteFile_thread != NULL)
1873 {
1874 return; //disable Play button if we're writing an audio file.
1875 }
1876
1877 if (playAudio_SoundStream == NULL)
1878 {
1879 playAudio_SoundCleanup ();
1880 main_playAudio_SoundInit (); //try to (re)start sound if it couldn't be initted before
1881 if (playAudio_SoundStream == NULL)
1882 {
1883 return; //still didn't init, so give up
1884 }
1885 gnaural_pauseflag = ~gnaural_pauseflag; //yes, a little silly, but necessary
1886 }
1887
1888 gnaural_pauseflag = ~gnaural_pauseflag;
1889 if (gnaural_pauseflag == 0)
1890 { // i.e., it was already paused:
1891 //continue playing sound:
1892 if (playAudio_SoundStream != NULL)
1893 {
1894 Pa_StartStream (playAudio_SoundStream);
1895 }
1896
1897 //Following is so I don't erase good stuff on screen until full UpdateLoopInforestart:
1898 if (((BB_InfoFlag) & BB_COMPLETED) != 0)
1899 {
1900 BB_InfoFlag &= ~BB_COMPLETED;
1901 BB_CurrentSampleCountLooped = 0;
1902 }
1903 gtk_button_set_label (button, "_Pause");
1904 GtkWidget *image =
1905 gtk_image_new_from_stock (GTK_STOCK_MEDIA_PAUSE, GTK_ICON_SIZE_BUTTON);
1906 gtk_button_set_image (GTK_BUTTON (button), image);
1907 main_UpdateGUI_Status ("Playing");
1908 }
1909 else
1910 { //e.g., it was not paused
1911 //stop playing sound:
1912 if (playAudio_SoundStream != NULL)
1913 {
1914 Pa_StopStream (playAudio_SoundStream);
1915 }
1916
1917 gtk_button_set_label (button, "_Play");
1918 GtkWidget *image =
1919 gtk_image_new_from_stock (GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON);
1920 gtk_button_set_image (GTK_BUTTON (button), image);
1921 main_UpdateGUI_Status ("Paused");
1922 }
1923 // bbl_UpdateGUI_Info ();
1924 }
1925
1926 /////////////////////////////////////////////////////
1927 //added 20100620, in order to break up main_VoiceInfoFormatter ()
1928 //for new list code (this loads main_sTmp)
1929 //NEED TO BE SURE tmstr IS TERMINATED!!!
main_VoiceInfoFormatter_type(SG_Voice * curVoice,char * tmstr)1930 void main_VoiceInfoFormatter_type (SG_Voice * curVoice, char *tmstr)
1931 {
1932 // main_sTmp[0]='\0';
1933 // sprintf (main_sTmp, "%d [", curVoice->ID);
1934 // sprintf (main_sTmp, "[", curVoice->ID);
1935 switch (curVoice->type)
1936 {
1937 case BB_VOICETYPE_BINAURALBEAT:
1938 strcat (tmstr, main_sBinauralBeat);
1939 break;
1940
1941 case BB_VOICETYPE_PINKNOISE:
1942 strcat (tmstr, main_sPinkNoise);
1943 break;
1944
1945 case BB_VOICETYPE_PCM:
1946 strcat (tmstr, main_sAudioFile);
1947 break;
1948
1949 case BB_VOICETYPE_ISOPULSE:
1950 strcat (tmstr, main_sIsochronicPulses);
1951 break;
1952
1953 case BB_VOICETYPE_ISOPULSE_ALT:
1954 strcat (tmstr, main_sIsochronicPulses_alt);
1955 break;
1956
1957 case BB_VOICETYPE_WATERDROPS:
1958 strcat (tmstr, main_sWaterDrops);
1959 break;
1960
1961 case BB_VOICETYPE_RAIN:
1962 strcat (tmstr, main_sRain);
1963 break;
1964 }
1965 }
1966
1967 /////////////////////////////////////////////////////
1968 //added 20070625, loads main_sTmp
main_VoiceInfoFormatter(SG_Voice * curVoice)1969 void main_VoiceInfoFormatter (SG_Voice * curVoice)
1970 {
1971 static char num[64];
1972
1973 if (curVoice != NULL)
1974 {
1975 sprintf (main_sTmp, "%d [", curVoice->ID);
1976 main_VoiceInfoFormatter_type (curVoice, main_sTmp);
1977 strcat (main_sTmp, "] ");
1978 strcat (main_sTmp, curVoice->description);
1979
1980 //add count to end:
1981 int dpcount_all = 0,
1982 dpcount_selected = 0;
1983
1984 SG_CountVoiceDPs (curVoice, &dpcount_all, &dpcount_selected);
1985 sprintf (num, " (%d,%d)", dpcount_all, dpcount_selected);
1986 strcat (main_sTmp, num);
1987 }
1988 else
1989 {
1990 main_sTmp[0] = '\0';
1991 }
1992 }
1993
1994 /////////////////////////////////////////////////////
1995 //negative amount rewinds, positive fast-forwards
1996 //"amount" should be between -1.0 and 1.0
main_OnButton_ForwardRewind(float amount)1997 void main_OnButton_ForwardRewind (float amount)
1998 {
1999 GN_Time_ResetSeniority ();
2000 if (amount < 0)
2001 {
2002 BB_ResetAllVoices ();
2003 }
2004 int i =
2005 BB_CurrentSampleCount + (BB_TotalDuration * amount * BB_AUDIOSAMPLERATE);
2006 if (i < 0)
2007 i = 0;
2008 else if (i > (BB_TotalDuration * BB_AUDIOSAMPLERATE))
2009 i = BB_TotalDuration * BB_AUDIOSAMPLERATE;
2010 BB_CurrentSampleCount = i;
2011 }
2012
2013 /////////////////////////////////////////////////////
main_OnButton_Stop(GtkButton * button)2014 void main_OnButton_Stop (GtkButton * button)
2015 {
2016 GN_Time_ResetSeniority ();
2017 BB_WriteStopFlag = 1; //try to kill a write if it is what's happening
2018 //first step is to pause:
2019 gnaural_pauseflag = 0; //to be sure bbl_OnButton_Play() thinks it is was running
2020 main_OnButton_Play (main_buttonPlayPause); // Simulate a user button push to enter paused state
2021
2022 //now make schedule and everything else go back to begining
2023 BB_Reset ();
2024 main_UpdateGUI_Status ("Stopped");
2025 //bbl_UpdateGUI_Info ();
2026 //gtk_label_set_text (LabelProgramStatus, "Program Stopped");
2027 }
2028
2029 /////////////////////////////////////////////////////
main_UpdateGUI_Status(char * msg)2030 void main_UpdateGUI_Status (char *msg)
2031 {
2032 sprintf (main_sTmp, "Status: %s", msg);
2033 gtk_label_set_text (main_labelStatus, main_sTmp);
2034 }
2035
2036 /////////////////////////////////////////////////////
2037 //20070105 updated to measure looping
main_UpdateGUI_ProjectedRuntime()2038 void main_UpdateGUI_ProjectedRuntime ()
2039 {
2040 const char sProjected[] = "Projected Runtime: ";
2041
2042 if (BB_Loops != 0)
2043 {
2044 int total = (int) (BB_TotalDuration * BB_Loops);
2045
2046 if (1 == BB_Loops)
2047 {
2048 sprintf (main_sTmp, "%s%d%s%d%s", sProjected, total / 60, main_sMin,
2049 total % 60, main_sSec);
2050 }
2051 else
2052 {
2053 sprintf (main_sTmp, "%s%d%s%d%s(%d%s%d%sx %d)", sProjected, total / 60,
2054 main_sMin, total % 60, main_sSec, ((int) BB_TotalDuration) / 60,
2055 main_sMin, ((int) BB_TotalDuration) % 60, main_sSec, BB_Loops);
2056 }
2057 gtk_label_set_text (main_labelTotalRuntime, main_sTmp);
2058 }
2059 else
2060 {
2061 sprintf (main_sTmp, "%sForever (%d%s%d%sx %s)", sProjected,
2062 ((int) BB_TotalDuration) / 60, main_sMin,
2063 ((int) BB_TotalDuration) % 60, main_sSec, main_sInf);
2064 gtk_label_set_text (main_labelTotalRuntime, main_sTmp);
2065 }
2066 }
2067
2068 /////////////////////////////////////////////////////
main_FormatProgressString()2069 void main_FormatProgressString ()
2070 {
2071 const char sProgress[] = "Progress: ";
2072
2073 const char sLoop[] = "(Loop: ";
2074
2075 int total =
2076 (BB_CurrentSampleCountLooped + BB_CurrentSampleCount) / BB_AUDIOSAMPLERATE;
2077 int remaining = (int) ((BB_TotalDuration * BB_Loops) - total);
2078
2079 if (BB_Loops != 0)
2080 {
2081 sprintf (main_sTmp, "%s%d%s%d%s Remaining: %d%s%d%s%s%d/%d)", sProgress,
2082 total / 60, main_sMin, total % 60, main_sSec, remaining / 60,
2083 main_sMin, remaining % 60, main_sSec, sLoop,
2084 BB_Loops - BB_LoopCount + 1, BB_Loops);
2085 }
2086 else
2087 {
2088 sprintf (main_sTmp, "%s%d%s%d%s%s%d/%s)", sProgress, (total / 60),
2089 main_sMin, total % 60, main_sSec, sLoop, (-BB_LoopCount) + 1,
2090 main_sInf);
2091 }
2092 }
2093
2094 //======================================
2095 //QUESTIONABLE BUG FIXES:
2096 //-BUG: figure out why resetting bb->InfoFlag's caused update labels timer to quit.
2097 // Wow, this one was a bit as if the garage door opened every time I pulled
2098 // my hair. Bizarre. One of the wierdest bugs I've ever seen.
2099 // SOLUTION: made BinauralBeat's InfoFlag an unsigned int (from int), made
2100 // explicit that the timer func returned TRUE, and rephrased bit setting line
2101 // back to what it had been when the bug first appeared (?!).
2102 // Hard to believe this really fixed it, but it no longer does the bad behavior. (20051020)
main_UpdateGUI_Labels()2103 void main_UpdateGUI_Labels ()
2104 {
2105
2106 //-----------------------------------------------------------
2107 //START stuff that gets updated EVERY second:
2108 //
2109 // current time point within schedule:
2110 main_FormatProgressString ();
2111 gtk_label_set_text (main_labelCurrentRuntime, main_sTmp);
2112
2113 //a20070620 Voice info:
2114 // main_VoiceInfoFormatter (SG_SelectVoice (NULL));
2115 // gtk_label_set_text (main_labelVoiceInfo, main_sTmp);
2116 //
2117 //END stuff that gets updated EVERY second:
2118 //-----------------------------------------------------------------------
2119
2120 //-----------------------------------------------------------------------
2121 //START check bb->InfoFlag
2122 //now update things that BinauralBeat says are changed:
2123 if (BB_InfoFlag != 0)
2124 {
2125 //.........................
2126 //if schedule is done:
2127 if (((BB_InfoFlag) & BB_COMPLETED) != 0)
2128 {
2129 // bbl_OnButton_Stop ();
2130 // gtk_label_set_text (LabelProgramStatus, "Schedule Completed");
2131 gnaural_pauseflag = 0; //so bbl_OnButton_Play() thinks I was running
2132 //main_OnButton_Play (); // I'm simulating user button push to pause
2133 main_OnButton_Stop (NULL);
2134 main_UpdateGUI_Status ("Schedule Completed");
2135 // bb->loopcount = bb->loops;
2136 // gtk_progress_bar_set_fraction (ProgressBar_Overall, 1.0);
2137
2138 }
2139
2140 //.........................
2141 //if starting a new loop:
2142 if (((BB_InfoFlag) & BB_NEWLOOP) != 0)
2143 {
2144 //reset the "new loop" bit of InfoFlag:
2145 BB_InfoFlag &= ~BB_NEWLOOP;
2146 //bbl_UpdateGUI_LoopInfo ();
2147 }
2148 } //END check bb->InfoFlag
2149 //-------------------------------------------------------------
2150 }
2151
2152 /////////////////////////////////////////////////////
main_UpdateGUI_Statusbar(const char * msg1,const char * msg2)2153 void main_UpdateGUI_Statusbar (const char *msg1, const char *msg2)
2154 {
2155 // g_print ("Trying to update statusbar\n");
2156 // sprintf(main_sTmp, "Trying to update statusbar\n");
2157 gint context_id = gtk_statusbar_get_context_id (main_Statusbar, "SC");
2158
2159 gtk_statusbar_pop (main_Statusbar, context_id);
2160 sprintf (main_sTmp, "%s %s", msg1, msg2);
2161 gtk_statusbar_push (main_Statusbar, context_id, main_sTmp);
2162 }
2163
2164 //======================================
2165 //following tries to be sure I don't end with a \ or / (which happens
2166 //only when getcwd returns a root like / or C:\, D:\ :
main_FixPathTerminator(char * pth)2167 void main_FixPathTerminator (char *pth)
2168 {
2169 // #ifdef GNAURAL_WIN32
2170 if (pth[(strlen (main_sPathCurrentWorking) - 1)] == '\\')
2171 {
2172 pth[(strlen (main_sPathCurrentWorking) - 1)] = '\0';
2173 }
2174 // #endif
2175 else
2176 // #ifndef GNAURAL_WIN32
2177 if (main_sPathCurrentWorking[(strlen (main_sPathCurrentWorking) - 1)] == '/')
2178 {
2179 main_sPathCurrentWorking[(strlen (main_sPathCurrentWorking) - 1)] = '\0';
2180 }
2181 // #endif
2182 }
2183
2184 //======================================
2185 //returns zero if file or path DOES exist
main_TestPathOrFileExistence(char * PathName)2186 int main_TestPathOrFileExistence (char *PathName)
2187 {
2188 struct stat buf;
2189
2190 return stat (PathName, &buf);
2191 }
2192
2193 //======================================
2194 //The most important thing this does is set main_sCurrentGnauralFilenameAndPath
2195 //e20070705: If bDefaultFlag == TRUE sets up $HOME/gnaural.schedule,
2196 //if FALSE, works with whatever is in main_sCurrentGnauralFilenameAndPath
main_SetupPathsAndFiles(int bDefaultFlag)2197 void main_SetupPathsAndFiles (int bDefaultFlag)
2198 {
2199 //Current Working Directory: [20100405: added check to avoid compiler complaints]
2200 if (NULL == getcwd (main_sPathCurrentWorking, PATH_MAX))
2201 {
2202 fprintf (stderr, "Couldn't get current working directory, errno: %i\n",
2203 errno);
2204 }
2205 main_FixPathTerminator (main_sPathCurrentWorking);
2206
2207 //DBGOUT(szDir_CurrentWorking);
2208
2209 //copy CWD to the other vars, just in case I forget to put anything in them:
2210 strcpy (main_sPathHome, main_sPathCurrentWorking);
2211 strcpy (main_sPathGnaural, main_sPathCurrentWorking);
2212
2213 SG_DBGOUT ("This was the command to launch Gnaural:");
2214 SG_DBGOUT (main_sPathExecutable);
2215
2216 SG_DBGOUT ("This is the current working directory:");
2217 SG_DBGOUT (main_sPathCurrentWorking);
2218
2219 #ifdef GNAURAL_WIN32
2220 //###########START WIN32-ONLY CODE#############
2221 //In Win32, I try to work from wherever the executable is located:
2222
2223 //-----
2224 //START of determine Gnaural's home directory:
2225 //HKEY_LOCAL_MACHINE\\Software\Microsoft\\Windows\CurrentVersion\\App Paths\\gnaural.exe
2226 //C:\Program Files\Gnaural;c:\Program Files\Common Files\GTK\2.0\bin
2227 HKEY hkey;
2228
2229 if (ERROR_SUCCESS ==
2230 RegOpenKeyEx (HKEY_LOCAL_MACHINE,
2231 "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\gnaural.exe",
2232 0, KEY_QUERY_VALUE, &hkey))
2233 {
2234 SG_DBGOUT ("Found gnaural.exe registry entry");
2235 DWORD type,
2236 count = sizeof (main_sPathTemp);
2237
2238 if (ERROR_SUCCESS ==
2239 RegQueryValueExA (hkey, "Path", 0, &type, (LPBYTE) main_sPathTemp,
2240 &count))
2241 {
2242 //Looking for the first path in the list:
2243 char *tmpch;
2244
2245 tmpch = strchr (main_sPathTemp, ';'); //search for first ';'
2246 if (tmpch != NULL)
2247 {
2248 (*tmpch) = '\0';
2249 }
2250 strcpy (main_sPathGnaural, main_sPathTemp);
2251 main_FixPathTerminator (main_sPathGnaural);
2252 }
2253 RegCloseKey (hkey);
2254 }
2255 else
2256 {
2257 //try a bruteforce approach:
2258 //20070919: I THINK THIS HAS main_TestPathOrFileExistence BACKWARD (not changing until I am sure)
2259 //20101201: finally changed it, i do think this was wrong and finally caused a problem on a user restricted machine:
2260 gchar winpathstr[] = "C:\\Program Files\\Gnaural";
2261 SG_DBGOUT_STR ("No gnaural.exe registry entry, checking manually for",
2262 winpathstr);
2263 if (0 == main_TestPathOrFileExistence (winpathstr))
2264 {
2265 SG_DBGOUT_STR (winpathstr, "did exist");
2266 strcpy (main_sPathGnaural, winpathstr);
2267 }
2268 else
2269 {
2270 SG_DBGOUT_STR (winpathstr, "did NOT exist");
2271 }
2272 }
2273
2274 /*
2275 //NOTE: Original intent (below commented code) was to have all file creation in executable's home directory, but
2276 //it failed because of "/" and "\" inconsistencies between WINE and Win32, rendering
2277 //mixed paths, etc. Since getcwd() seems to be mostly consistent on each
2278 //platform, I eventually stuck with that -- but in the end, a WINE bug causing gcwd to return
2279 //(in my case) H:\ for both root and my home directory made me give-up and just
2280 //use the (goddamned) registry.
2281
2282 strcpy (main_sPathTemp, szFile_ExecutablePath);
2283
2284 //Now make all slashes consistent with DOS:
2285 int i=strlen(main_sPathTemp);
2286 while ((--i)>-1) if (main_sPathTemp[i]=='/') main_sPathTemp[i]='\\';
2287
2288 //Now throw away executable name if there is any:
2289 char *tmpch;
2290 tmpch=strrchr(main_sPathTemp,'\\'); //search for first slash from end
2291 if (tmpch!=NULL) {
2292 (*tmpch)='\0';
2293
2294 GNAURAL_DBGOUT("This is the directory Gnaural is being run from:");
2295 GNAURAL_DBGOUT(main_sPathTemp);
2296
2297 //now return all slashes to DOS style:
2298 i=strlen(main_sPathTemp);
2299 while ((--i)>-1) if (main_sPathTemp[i]=='/') main_sPathTemp[i]='\\';
2300
2301 //now change in to that directory [still necessary?!]
2302 chdir(main_sPathTemp);
2303
2304 //now ADD starting slash if it isn't there:
2305 if (main_sPathTemp[0]!='\\')
2306 {
2307 for (i=strlen(main_sPathTemp);i>-1;i--) main_sPathTemp[i+1]=main_sPathTemp[i];
2308 main_sPathTemp[0]='\\';
2309 }
2310 }
2311 //If I got here, user is already working from Gnaural's directory:
2312 else main_sPathTemp[0]='\0';
2313
2314 //now attach this string to the end of CWD:
2315 strcpy(szDir_Gnaural,szDir_CurrentWorking);
2316 strcat(szDir_Gnaural,main_sPathTemp);
2317 */
2318
2319 //END of determine Gnaural's home directory
2320 //-----
2321
2322 //Finally, setup file and path to gnaural_schedule.txt file:
2323 if (bDefaultFlag == TRUE)
2324 {
2325 sprintf (main_sCurrentGnauralFilenameAndPath, "%s\\schedule.gnaural",
2326 main_sPathGnaural);
2327 }
2328 SG_DBGOUT ("Constructing default schedule path:");
2329 SG_DBGOUT (main_sCurrentGnauralFilenameAndPath);
2330 return;
2331 //###########END WIN32-ONLY CODE#############
2332 #endif
2333
2334 #ifndef GNAURAL_WIN32
2335 //###########START 'NUX-ONLY CODE#############
2336 //In Windows, Gnaural just does everything in it's formal installation directory.
2337 //In 'nix, it works from ~/.gnaural
2338
2339 //figure out the paths/directories needed for all file access:
2340 if (NULL != getenv ("HOME"))
2341 {
2342 //Home directory:
2343 strcpy (main_sPathHome, getenv ("HOME"));
2344 //~/.gnaural directory:
2345 sprintf (main_sPathGnaural, "%s/.gnaural", main_sPathHome);
2346 }
2347 else
2348 { //failed, so harmlessly put current directory in there...
2349 SG_ERROUT ("Couldn't determine home directory");
2350 strcat (main_sPathHome, main_sPathCurrentWorking);
2351 strcat (main_sPathGnaural, main_sPathCurrentWorking);
2352 }
2353
2354 //create the .gnaural directory if it doesn't exist yet:szDir_CurrentWorking
2355 if (0 != main_TestPathOrFileExistence (main_sPathGnaural))
2356 {
2357 SG_DBGOUT ("Creating ~/.gnaural directory");
2358
2359 if (0 != mkdir (main_sPathGnaural, 0777))
2360 {
2361 fprintf (stderr, "Couldn't make ~/.gnaural directory %s: errno=%i\n",
2362 main_sPathGnaural, errno);
2363 }
2364 }
2365 else
2366 {
2367 SG_DBGOUT ("Found ~/.gnaural directory; will use.");
2368 }
2369 //setup file and path to gnaural_schedule.txt file:
2370 if (bDefaultFlag == TRUE)
2371 {
2372 sprintf (main_sCurrentGnauralFilenameAndPath, "%s/schedule.gnaural",
2373 main_sPathGnaural);
2374 }
2375 SG_DBGOUT ("Accessing schedule file: ");
2376 SG_DBGOUT (main_sCurrentGnauralFilenameAndPath);
2377 return;
2378 #endif
2379 //###########END 'NUX-ONLY CODE#############
2380 }
2381
2382 /////////////////////////////////////////////////////
2383 //20070824 Highly improved Vol-Bal routine taken from Gnaural
main_ProcessVolBal()2384 void main_ProcessVolBal ()
2385 {
2386 BB_VolumeOverall_right = BB_VolumeOverall_left = main_OverallVolume;
2387
2388 if (main_OverallBalance > 0)
2389 { //that means attenuate Left:
2390 BB_VolumeOverall_left *= (1.f - fabs (main_OverallBalance));
2391 }
2392 else
2393 { //that means attenuate Right:
2394 BB_VolumeOverall_right *= (1.f - fabs (main_OverallBalance));
2395 }
2396 }
2397
2398 /////////////////////////////////////////////////////
main_on_hscaleBalance(float range)2399 void main_on_hscaleBalance (float range)
2400 {
2401 main_OverallBalance = range *= .01;
2402 main_ProcessVolBal ();
2403 }
2404
2405 /////////////////////////////////////////////////////
main_on_hscaleVolume(float range)2406 void main_on_hscaleVolume (float range)
2407 {
2408 main_OverallVolume = range *= .01;
2409 main_ProcessVolBal ();
2410 }
2411
2412 /////////////////////////////////////////////////////
2413 //Main graphtype func. Sets global SG_GraphType to the appropriate
2414 //define from the given togglebutton and redraws graph to new view.
2415 //Used to determine graph type from user selection
main_SetGraphType(GtkToggleButton * togglebutton)2416 void main_SetGraphType (GtkToggleButton * togglebutton)
2417 {
2418 gtk_toggle_button_set_active (togglebutton, TRUE);
2419 if (togglebutton == main_togglebuttonViewFreq)
2420 {
2421 SG_GraphType = SG_GRAPHTYPE_BASEFREQ;
2422 }
2423 else if (togglebutton == main_togglebuttonViewBeat)
2424 {
2425 SG_GraphType = SG_GRAPHTYPE_BEATFREQ;
2426 }
2427 else if (togglebutton == main_togglebuttonViewVol)
2428 {
2429 SG_GraphType = SG_GRAPHTYPE_VOLUME;
2430 }
2431 else if (togglebutton == main_togglebuttonViewBal)
2432 {
2433 SG_GraphType = SG_GRAPHTYPE_VOLUME_BALANCE;
2434 };
2435 SG_ConvertDataToXY (main_drawing_area);
2436 SG_DrawGraph (main_drawing_area);
2437 }
2438
2439 /////////////////////////////////////////////////////
2440 //Sets graph view toggle according to SG_GraphType
main_SetGraphToggle(GtkWidget * widget)2441 void main_SetGraphToggle (GtkWidget * widget)
2442 {
2443 switch (SG_GraphType)
2444 {
2445 case SG_GRAPHTYPE_BASEFREQ:
2446 main_SetGraphType (main_togglebuttonViewFreq);
2447 break;
2448
2449 case SG_GRAPHTYPE_VOLUME:
2450 main_SetGraphType (main_togglebuttonViewVol);
2451 break;
2452
2453 case SG_GRAPHTYPE_VOLUME_BALANCE:
2454 main_SetGraphType (main_togglebuttonViewBal);
2455 break;
2456
2457 case SG_GRAPHTYPE_BEATFREQ:
2458 main_SetGraphType (main_togglebuttonViewBeat);
2459 break;
2460
2461 default:
2462 main_SetGraphType (main_togglebuttonViewFreq);
2463 break;
2464 }
2465 SG_ConvertDataToXY (widget);
2466 SG_DrawGraph (widget);
2467 }
2468
2469 /////////////////////////////////////////////////////
2470 //Basically, a way to get a quick "yes or no" from user, or just present some info.
2471 //Returns 1 for "yes", 0 for "no"
main_MessageDialogBox(char * msg,GtkMessageType messagetype,GtkButtonsType buttonformation)2472 int main_MessageDialogBox (char *msg,
2473 GtkMessageType messagetype,
2474 GtkButtonsType buttonformation)
2475 {
2476 GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
2477 GTK_DIALOG_DESTROY_WITH_PARENT,
2478 messagetype,
2479 buttonformation,
2480 "%s", msg);
2481
2482 // "Error loading file '%s': %s", main_sTmp, g_strerror (errno));
2483 gint res = gtk_dialog_run (GTK_DIALOG (dialog));
2484
2485 gtk_widget_destroy (dialog);
2486
2487 if (GTK_RESPONSE_OK == res || GTK_RESPONSE_YES == res ||
2488 GTK_RESPONSE_APPLY == res || GTK_RESPONSE_ACCEPT == res)
2489 {
2490 return 1;
2491 }
2492 else
2493 return 0;
2494 }
2495
2496 //========================================
2497 //NOTE: THIS OPTION WAS DEACTIVATED IN THE GLADE FILE 20071203
main_on_export_mp3_activate()2498 void main_on_export_mp3_activate ()
2499 {
2500 //This is experimental, requires LAME, and runs a second instance of Gnaural in the background;
2501 //Sorry, Windows users - I don't have a way to hack this for you yet :-(
2502
2503 main_MessageDialogBox
2504 ("Sorry, Gnaural can't make MP3's yet, but you can make a WAV file then covert it to MP3 with LAME\nhttp://lame.sourceforge.net",
2505 GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
2506 return;
2507
2508 #ifdef GNAURAL_WIN32
2509 main_MessageDialogBox
2510 ("Currently, the Windows version can only do this at the command line. Be sure LAME is installed\n(see http://lame.sourceforge.net/),\nthen type:\n\n\tgnaural -o | lame - MyMeditation.mp3",
2511 GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
2512 return;
2513 #endif
2514
2515 if (BB_Loops < 1)
2516 {
2517 if (0 ==
2518 main_MessageDialogBox
2519 ("You are in endless-loop mode. Do you really want to fill all the space on your hard drive?",
2520 GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO))
2521 {
2522 return; //don't start write an infinitely large file
2523 }
2524 }
2525
2526 //TODO: check automatically for LAME
2527 if (0 ==
2528 main_MessageDialogBox
2529 ("Do you have LAME installed? If not, this step will fail.\nSee http://lame.sourceforge.net/",
2530 GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO))
2531 {
2532 return;
2533 }
2534 //TODO check disk space for user, update GUI status window to tell that writing is going on
2535
2536 //Ask for a name for the MP3 file:
2537 GtkWidget *dialog;
2538
2539 dialog =
2540 gtk_file_chooser_dialog_new ("Save MP3 Audio File",
2541 GTK_WINDOW (main_window),
2542 GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
2543 GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE,
2544 GTK_RESPONSE_ACCEPT, NULL);
2545 //gtk_entry_set_text (GTK_ENTRY (entry_Input), "Gnaural.wav");
2546 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
2547 main_sPathCurrentWorking);
2548 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "Gnaural.mp3");
2549
2550 //gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);//doesn't exist yet in Debian unstable
2551
2552 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2553 {
2554 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2555
2556 if (strlen (filename) > 0)
2557 {
2558 //this works great, just doesn't give user any way to know when it is done, and (worse) doesn't always reliably find it's own executable.
2559 //###############FOR LINUX################
2560 #ifndef GNAURAL_WIN32
2561 sprintf (main_sPathTemp, "%s -o | lame - %s &", main_sPathExecutable,
2562 filename);
2563 //[20100405: added check to avoid compiler complaints]:
2564 if (0 == system (main_sPathTemp))
2565 {
2566 }
2567 #endif
2568 //###############FOR WIN32################
2569 #ifdef GNAURAL_WIN32
2570 sprintf (main_sPathTemp, "%s -o | lame - %s", main_sPathExecutable,
2571 filename);
2572 system (main_sPathTemp);
2573 // _spawn(main_sPathTemp);
2574 //_spawnlp (_P_DETACH, szFile_ExecutablePath, "-o", "|", "lame","-", filename, NULL);
2575 //_execlp (szFile_ExecutablePath, "-o", "|", "lame","-", filename, NULL);
2576 /*
2577 ShellExecute(0,
2578 "open", // Operation to perform
2579 main_sPathTemp, // Application name
2580 NULL, // Additional parameters
2581 0, // Default directory
2582 SW_SHOW);
2583 */
2584 #endif
2585
2586 main_MessageDialogBox
2587 ("A second copy of Gnaural has be spawned, and will run in the background until finished. In case of a problem, you can kill the processes with:\nkillall gnaural",
2588 GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
2589 }
2590 g_free (filename);
2591 }
2592 gtk_widget_destroy (dialog);
2593 }
2594
2595 //START writethreadstuff======================================
2596 /////////////////////////////////////////////////////
main_on_export_audio_to_file_activate()2597 void main_on_export_audio_to_file_activate ()
2598 {
2599 char *filename = NULL;
2600
2601 if (0 != main_CheckSndfileVersion ())
2602 {
2603 return;
2604 }
2605
2606 if (BB_Loops < 1)
2607 {
2608 if (0 ==
2609 main_MessageDialogBox
2610 ("You are in endless-loop mode. Do you really want to fill all the space on your hard drive?",
2611 GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO))
2612 {
2613 return; //can't write an infinitely large file! (COULD USE A DIALOG)
2614 }
2615 }
2616 if (main_AudioWriteFile_thread != NULL)
2617 {
2618 main_MessageDialogBox ("You are already writing a file.",
2619 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK);
2620 return; //this means someone is already writing (COULD USE A DIALOG)
2621 }
2622 //TODO check disk space for user, update GUI status window to tell that writing is going on
2623 GtkWidget *dialog;
2624
2625 dialog =
2626 gtk_file_chooser_dialog_new ("Save Audio File",
2627 GTK_WINDOW (main_window),
2628 GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL,
2629 GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE,
2630 GTK_RESPONSE_ACCEPT, NULL);
2631
2632 //create a Box to put some extra stuff in:
2633 GtkWidget *box = gtk_vbox_new (FALSE, 0);
2634
2635 gtk_widget_show (box);
2636 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), box);
2637
2638 //add some information for user
2639 GtkWidget *label_Info =
2640 gtk_label_new
2641 ("Filename extension will be appended automatically according to format chosen below:");
2642 gtk_widget_show (label_Info);
2643 gtk_box_pack_start (GTK_BOX (box), label_Info, FALSE, FALSE, 0);
2644
2645 GtkWidget *comboboxentry1 = gtk_combo_box_entry_new_text ();
2646
2647 gtk_widget_show (comboboxentry1);
2648
2649 int ComboboxEntryCount = 0;
2650 static int ComboboxFiletypeSelection = -1;
2651 char *strFileExtension = 0;
2652
2653 //START standard sndfile list code vars=====================
2654 SF_FORMAT_INFO info;
2655 SF_INFO sfinfo;
2656 char buffer[128];
2657 int format,
2658 major_count,
2659 subtype_count,
2660 m,
2661 s;
2662
2663 sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int));
2664 sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count,
2665 sizeof (int));
2666 //END standard sndfile list code vars=====================
2667
2668 //START standard sndfile list code=====================
2669 //clean everything up before filling it
2670 memset (&sfinfo, 0, sizeof (sfinfo));
2671 buffer[0] = 0;
2672 sfinfo.channels = 2;
2673
2674 for (m = 0; m < major_count; m++)
2675 {
2676 info.format = m;
2677 sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info));
2678 format = info.format;
2679 for (s = 0; s < subtype_count; s++)
2680 {
2681 info.format = s;
2682 sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &info, sizeof (info));
2683
2684 //put the whole number together and let sfinfo hold it:
2685 format = (format & SF_FORMAT_TYPEMASK) | info.format;
2686
2687 sfinfo.format = format;
2688 //--------put my stuff here:
2689 //fill comboboxes with all compatible formats:
2690 if (main_CheckSndfileFormat (sfinfo.format))
2691 {
2692 //now do this over again in order to get the name:
2693 info.format = m;
2694 sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info));
2695
2696 SG_DBGOUT_STR ("Adding format:", info.name);
2697 gtk_combo_box_append_text (GTK_COMBO_BOX (comboboxentry1), info.name);
2698 //if this is first time user is doing this, set default to whatever my default was:
2699 if (-1 == ComboboxFiletypeSelection
2700 && (main_AudioWriteFile_format & SF_FORMAT_TYPEMASK) == info.format)
2701 {
2702 ComboboxFiletypeSelection = ComboboxEntryCount;
2703 SG_DBGOUT ("[Defaulting to above format]");
2704 }
2705 ++ComboboxEntryCount;
2706 }
2707 else
2708 {
2709 SG_DBGOUT_STR ("NOT adding format:", info.name);
2710 }
2711 //--------end my stuff
2712 };
2713 };
2714 //END standard sndfile list code=====================
2715
2716 //add the usual filters for sndfile's formats:
2717 //old way:
2718 //main_DialogAddFileFilters (dialog,"~Gnaural Audio Files~*.wav,*.aiff,*.au,*.flac,*.ogg,~All Files~*");
2719 //new way:
2720 main_MakeAudioFormatsFileFilterString (main_sTmp, sizeof (main_sTmp));
2721 main_DialogAddFileFilters (dialog, main_sTmp);
2722
2723 //make ComboboxFiletypeSelection hightlight last chosen comboboxentry for format:
2724 gtk_combo_box_set_active ((GtkComboBox *) comboboxentry1,
2725 ComboboxFiletypeSelection);
2726 gtk_box_pack_end (GTK_BOX (box), comboboxentry1, FALSE, FALSE, 0);
2727
2728 //extract base from filename for convenience:
2729 filename = g_path_get_basename (main_AudioWriteFile_name);
2730 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), filename);
2731 g_free (filename);
2732
2733 //add some filters for user's convenience:
2734 //old way:
2735 // main_DialogAddFileFilters (dialog,
2736 // "~Gnaural Audio Files~*.wav,*.aiff,*.au,*.flac,*.ogg,~All Files~*");
2737
2738 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2739 {
2740 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2741 if (strlen (filename) > 0)
2742 {
2743 ComboboxFiletypeSelection =
2744 gtk_combo_box_get_active ((GtkComboBox *) comboboxentry1);
2745
2746 //now get ComboboxFiletypeSelection extension and sndfile type to write:
2747 main_sTmp[0] = '\0';
2748 ComboboxEntryCount = 0;
2749
2750 //START standard sndfile list code=====================
2751 //clean vars before filling them:
2752 memset (&sfinfo, 0, sizeof (sfinfo));
2753 buffer[0] = 0;
2754 sfinfo.channels = 2;
2755
2756 for (m = 0; m < major_count; m++)
2757 {
2758 info.format = m;
2759 sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info));
2760 format = info.format;
2761 for (s = 0; s < subtype_count; s++)
2762 {
2763 info.format = s;
2764 sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &info, sizeof (info));
2765
2766 format = (format & SF_FORMAT_TYPEMASK) | info.format;
2767
2768 sfinfo.format = format;
2769 //--------put my stuff here:
2770 if (main_CheckSndfileFormat (sfinfo.format))
2771 {
2772 info.format = m;
2773 sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info));
2774 if (ComboboxEntryCount == ComboboxFiletypeSelection)
2775 {
2776 main_AudioWriteFile_format = sfinfo.format;
2777 sprintf (main_sTmp, ".%s", info.extension);
2778 strFileExtension = main_sTmp;
2779 m = major_count;
2780 break; //critical so that info.* data above doesn't change
2781 }
2782 ++ComboboxEntryCount;
2783 }
2784 //--------end my stuff
2785 };
2786 };
2787 //END standard sndfile list code=====================
2788
2789 //now, check for extension, if there is one, see if it is same
2790 //as ComboboxFiletypeSelection; if so do nothing, otherwise add correct one here:
2791 SG_StringAllocateAndCopy (&main_AudioWriteFile_name, filename);
2792 //asign address of last character of string to ext:
2793 char *ext = &(main_AudioWriteFile_name[strlen (main_AudioWriteFile_name)]);
2794
2795 //count backward through string until either first character is found OR a "." is found:
2796 for (; main_AudioWriteFile_name != ext && ext[0] != '.'; ext--);
2797 //if no "." was found, leave it alone:
2798 if (ext == main_AudioWriteFile_name)
2799 {
2800 BB_DBGOUT ("Filename has no extension");
2801 }
2802 else
2803 //if a "." was found:
2804 {
2805 //make sure really removing a Gnaural-understood extension:
2806 BB_DBGOUT_STR ("Previous file extension:", ext);
2807 for (m = 0; m < major_count; m++)
2808 {
2809 info.format = m;
2810 sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info));
2811 if (0 == strncasecmp (&ext[1], info.extension, strlen (info.extension)))
2812 {
2813 //TODO: it would be nice if I alerted user IF their extension
2814 //is different from the SF_AUDIOFORMAT they "chose", because
2815 //I just replace it anyway:
2816 BB_DBGOUT ("Extension known - removing");
2817 *ext = '\0'; //"remove" extension
2818 break;
2819 }
2820 }
2821
2822 if (m >= major_count)
2823 {
2824 BB_DBGOUT ("Extension unknown - not removing");
2825 }
2826 }
2827
2828 //put an extension on:
2829 strcpy (main_sPathTemp, main_AudioWriteFile_name);
2830 strcat (main_sPathTemp, strFileExtension);
2831
2832 //see if file already exists:
2833 if (0 == main_TestPathOrFileExistence (main_sPathTemp) &&
2834 0 == main_MessageDialogBox (main_sPathTemp,
2835 GTK_MESSAGE_INFO, GTK_BUTTONS_YES_NO))
2836 {
2837 BB_DBGOUT ("File already exists, user aborted Write");
2838 }
2839 else
2840 {
2841 SG_StringAllocateAndCopy (&main_AudioWriteFile_name, main_sPathTemp);
2842 BB_DBGOUT_STR ("New Filename:", main_AudioWriteFile_name);
2843 main_OnButton_Stop (NULL);
2844 main_AudioWriteFile_start ();
2845 }
2846 }
2847 g_free (filename);
2848 }
2849 gtk_widget_destroy (dialog);
2850 }
2851
2852 /////////////////////////////////////////////////////
2853 //Only interested in SubTypes SF_FORMAT_PCM_16 and SF_FORMAT_VORBIS:
main_CheckSndfileFormat(int format)2854 int main_CheckSndfileFormat (int format)
2855 {
2856 SF_INFO sfinfo;
2857
2858 memset (&sfinfo, 0, sizeof (sfinfo));
2859 sfinfo.channels = 2;
2860 sfinfo.format = format;
2861
2862 // sfinfo.format = (formatMajor & SF_FORMAT_TYPEMASK) | format;
2863 //Now check for either SF_FORMAT_PCM_16 or SF_FORMAT_VORBIS
2864 if (SF_FORMAT_PCM_16 != (format & SF_FORMAT_SUBMASK)
2865 && SF_FORMAT_VORBIS != (format & SF_FORMAT_SUBMASK))
2866 return FALSE;
2867 return sf_format_check (&sfinfo);
2868 }
2869
2870 //========================================
main_AudioWriteFile(void * ptr)2871 void main_AudioWriteFile (void *ptr)
2872 {
2873 BB_DBGOUT ("Starting Audio Write File thread");
2874 BB_DBGOUT_INT ("Audio SF_FORMAT type:", main_AudioWriteFile_format);
2875 //BB_WriteWAVFile (main_AudioWriteFile_name); //OLD WAY, replaced 20071130
2876 exportAudioToFile (main_AudioWriteFile_name, main_AudioWriteFile_format);
2877 main_AudioWriteFile_thread = NULL; //so rest of program knows writing is done
2878 BB_DBGOUT ("Audio Write File thread exiting");
2879 g_thread_exit (0); //this should be updated to g_thread_join(GThread) when I get more time
2880 }
2881
2882 //======================================
main_AudioWriteFile_start()2883 void main_AudioWriteFile_start ()
2884 {
2885 if (main_AudioWriteFile_thread != NULL)
2886 {
2887 main_MessageDialogBox ("You are already writing one", GTK_MESSAGE_ERROR,
2888 GTK_BUTTONS_OK);
2889 return; //this means someone is already writing
2890 }
2891 //Note: you must be in paused state to do writing!
2892 //bbl_UpdateGUI_Info ();
2893
2894 //user can set this to non-0 at any time to stop write:
2895 BB_WriteStopFlag = 0;
2896 //new gthread code (new 20051126):
2897 GError *thread_err = NULL;
2898
2899 if (NULL ==
2900 (main_AudioWriteFile_thread
2901 =
2902 g_thread_create ((GThreadFunc) main_AudioWriteFile, (void *) NULL, TRUE,
2903 &thread_err)))
2904 {
2905 fprintf (stderr, "g_thread_create failed: %s!!\n", thread_err->message);
2906 g_error_free (thread_err);
2907 }
2908 }
2909
2910 //END writethreadstuff======================================
2911
2912 //====START OF IMPORT OLD GNAURAL FILE SECTION====
2913 //======================================
2914 //this just reads the values found between brackets []
main_GNAURAL1FILE_ParseCmd(FILE * stream)2915 void main_GNAURAL1FILE_ParseCmd (FILE * stream)
2916 {
2917 char strNum[128];
2918
2919 char strCmd[128];
2920
2921 char tmpchar;
2922
2923 int inum = 0,
2924 icmd = 0;
2925
2926 strNum[0] = strCmd[0] = '\0';
2927 while ((tmpchar = fgetc (stream)) != ']' && feof (stream) == 0)
2928 {
2929 //eat up any extra characters
2930 //while (tmpchar=='=' || ' ') tmpchar=fgetc( stream );
2931 //if it is a number, put it in the number string:
2932 if ((tmpchar >= '0' && tmpchar <= '9') || tmpchar == '.' || tmpchar == '-')
2933 {
2934 strNum[inum] = tmpchar;
2935 inum++;
2936 }
2937
2938 //if it is a string, put it in Cmd string:
2939 else
2940 if ((tmpchar >= 'A'
2941 && tmpchar <= 'Z') || (tmpchar >= 'a' && tmpchar <= 'z'))
2942 {
2943 strCmd[icmd] = toupper (tmpchar);
2944 icmd++;
2945 }
2946 else if (tmpchar == '#')
2947 { //user put a comment after a command!
2948 while (feof (stream) == 0 && tmpchar != '\n')
2949 tmpchar = fgetc (stream);
2950 return;
2951 }
2952 } //end of parsing line:
2953
2954 strNum[inum] = '\0';
2955 strCmd[icmd] = '\0';
2956 if (!strcmp (strCmd, "BASEFREQ"))
2957 {
2958 OldGnauralVars.FreqBase = (float) g_ascii_strtod (strNum, NULL);
2959 if (OldGnauralVars.FreqBase < 40)
2960 OldGnauralVars.FreqBase = 40;
2961 if (OldGnauralVars.FreqBase > 1000)
2962 OldGnauralVars.FreqBase = 1000;
2963 }
2964
2965 if (!strcmp (strCmd, "TONEVOL"))
2966 {
2967 OldGnauralVars.Volume_Tone = (float) g_ascii_strtod (strNum, NULL);
2968 if (OldGnauralVars.Volume_Tone < 0)
2969 OldGnauralVars.Volume_Tone = 0;
2970 if (OldGnauralVars.Volume_Tone > 100)
2971 OldGnauralVars.Volume_Tone = 100;
2972 // OldGnauralVars.Volume_Tone = (int) (163.84 * ftemp + 0.5);
2973 OldGnauralVars.Volume_Tone = (float) (OldGnauralVars.Volume_Tone * 0.01);
2974 }
2975
2976 if (!strcmp (strCmd, "NOISEVOL"))
2977 {
2978 OldGnauralVars.Volume_Noiz = (float) g_ascii_strtod (strNum, NULL);
2979 if (OldGnauralVars.Volume_Noiz < 0)
2980 OldGnauralVars.Volume_Noiz = 0;
2981 if (OldGnauralVars.Volume_Noiz > 100)
2982 OldGnauralVars.Volume_Noiz = 100;
2983 OldGnauralVars.Volume_Noiz = (float) (OldGnauralVars.Volume_Noiz * 0.01);
2984 }
2985
2986 if (!strcmp (strCmd, "LOOPS"))
2987 {
2988 OldGnauralVars.loops = atoi (strNum);
2989 if (OldGnauralVars.loops < 0)
2990 OldGnauralVars.loops = 0;
2991 OldGnauralVars.loopcount = OldGnauralVars.loops;
2992 }
2993 }
2994
2995 //======================================
main_GNAURAL1FILE_SchedBuffToSchedule(char * str)2996 void main_GNAURAL1FILE_SchedBuffToSchedule (char *str)
2997 {
2998 //the philosophy here: take a string with commas separating the
2999 //floats, hunt for commas. When one is found, start a new string
3000 //beginning with next character. Take every following character up
3001 //to (but not including) the next comma. Then store that float, dispose
3002 //of the tempstring, and hunt some more until length of string is
3003 //covered.
3004 char tmpstr[256];
3005
3006 float *tmpfloat = NULL;
3007
3008 char substr[256];
3009
3010 //first count how many floats are in the string:
3011 int floatcount = 1;
3012
3013 unsigned long i;
3014
3015 for (i = 0; i < strlen (str); i++)
3016 {
3017 if (str[i] == ',')
3018 {
3019 floatcount++;
3020 } // end if comma
3021 } //end for i
3022 //do it all again, now that I know how many floats:
3023 tmpfloat = (float *) malloc (floatcount * sizeof (float));
3024 floatcount = 0;
3025 int j = 0;
3026
3027 for (i = 0; i < strlen (str); i++)
3028 {
3029 //first extract a number to a string:
3030 if ((str[i] >= '0' && str[i] <= '9') || str[i] == '.' || str[i] == '-')
3031 tmpstr[j++] = str[i];
3032 //if I found a comma, end the string:
3033 else if (str[i] == ',')
3034 {
3035 tmpstr[j] = '\0';
3036 strcpy (substr, tmpstr);
3037 tmpfloat[floatcount] = (float) g_ascii_strtod (substr, NULL);
3038 j = 0;
3039 floatcount++;
3040 } // end if comma
3041 } //end for i
3042 if (j != 0)
3043 { //there should be one more float to get:
3044 tmpstr[j] = '\0';
3045 strcpy (substr, tmpstr);
3046 tmpfloat[floatcount] = (float) g_ascii_strtod (substr, NULL);
3047 ++floatcount;
3048 } // end if j!=0
3049 //==START LOADING THE FLOATS IN TO THE GRAPH:
3050 OldGnauralVars.ScheduleEntriesCount = floatcount / 3;
3051 //Now init SG so that it can start to receive the schedule.
3052 //Since this is an old Gnaural file, there must be only two voices, first being
3053 //the explicitly directed binaural beat, the second simple unmodulated noise. The
3054 //philosophy of Gnaural file opening is to let the Graph (SG) always lead the BB
3055 //engine, so load the graph by creating 2 voices with SG_SetupDefaultDataPoints(),
3056 //then delete all DPs and start loading adding real ones to the end (the noise one only
3057 //has one DP, with the volume set).
3058 SG_SetupDefaultDataPoints (2); //first voice is BB data, second will be noise
3059 //important: cleanup all datapoints (since new data will be instated), and NULL first DPs:
3060 SG_CleanupDataPoints (SG_FirstVoice->FirstDataPoint);
3061 SG_FirstVoice->FirstDataPoint = NULL;
3062 SG_CleanupDataPoints (SG_FirstVoice->NextVoice->FirstDataPoint);
3063 SG_FirstVoice->NextVoice->FirstDataPoint = NULL;
3064 //for reference:
3065 /*
3066 struct {
3067 float FreqBase;
3068 float Volume_Tone;
3069 float Volume_Noiz;
3070 int loops;
3071 int loopcount;
3072 int StereoNoiz;
3073 int ScheduleEntriesCount;
3074 } OldGnauralVars;
3075 */
3076 //==START load BinauralBeat voice:
3077 int buffcount = 0;
3078
3079 float FreqLStart,
3080 FreqRStart,
3081 Duration,
3082 Duration_total = 0;
3083
3084 int entry;
3085
3086 SG_DataPoint *curDP = NULL;
3087
3088 for (entry = 0; entry < OldGnauralVars.ScheduleEntriesCount; entry++)
3089 {
3090 //get the buffer values, put them in temporary storage:
3091 FreqLStart = (float) tmpfloat[buffcount++];
3092 FreqRStart = (float) tmpfloat[buffcount++];
3093 Duration = (float) tmpfloat[buffcount++];
3094 Duration_total += Duration;
3095 //apply temporary storage to a real: DP
3096 curDP = SG_AddNewDataPointToEnd (SG_FirstVoice->FirstDataPoint, SG_FirstVoice, Duration, //duration
3097 fabs (FreqLStart - FreqRStart), //beatfreq
3098 OldGnauralVars.Volume_Tone, //volume_left
3099 OldGnauralVars.Volume_Tone, //volume_right
3100 OldGnauralVars.FreqBase, //basefreq
3101 SG_UNSELECTED); //state
3102 if (SG_FirstVoice->FirstDataPoint == NULL)
3103 SG_FirstVoice->FirstDataPoint = curDP;
3104 } //end for
3105 //==END load BinauralBeat voice
3106
3107 //==START load Noise voice:
3108 curDP = SG_AddNewDataPointToEnd (SG_FirstVoice->NextVoice->FirstDataPoint, SG_FirstVoice->NextVoice, Duration_total, //duration
3109 0, //beatfreq
3110 OldGnauralVars.Volume_Noiz, //volume_left
3111 OldGnauralVars.Volume_Noiz, //volume_right
3112 0, //basefreq
3113 SG_UNSELECTED); //state
3114 SG_FirstVoice->NextVoice->FirstDataPoint = curDP;
3115 //==END load Noise voice:
3116 //==END LOADING THE FLOATS IN TO THE GRAPH:
3117 main_SetLoops (OldGnauralVars.loops);
3118 // SG_DBGOUT("Got to line 1934");
3119 free (tmpfloat);
3120 } //end of extractfloats()
3121
3122 //======================================
3123 //THIS IS INCLUDED SOLEY TO OPEN OLD STYLE GNAURAL SCHEDULES
3124 //opens whatever is passed as filename, dumps it in to str,
3125 //then uses SchedBuffToSchedule to fill Schedule. has to go like this
3126 //because it has to segregate all "command strings" [] from schedule entries
main_GNAURAL1FILE_SchedFilenameToSchedule(char * filename)3127 int main_GNAURAL1FILE_SchedFilenameToSchedule (char *filename)
3128 {
3129 FILE *stream;
3130
3131 char *str;
3132
3133 if ((stream = fopen (filename, "r")) == NULL)
3134 { //didn't find a schedule file, so return (could try to create one at this point with WriteScheduleToFile()
3135 return 0;
3136 }
3137 //FileFlag = true;
3138
3139 //START count file elements to know how big to make the char buffer:
3140 char tmpchar;
3141
3142 unsigned int i = 0;
3143
3144 while (feof (stream) == 0)
3145 {
3146 tmpchar = fgetc (stream);
3147 //comments: eat entire lines starting with #:
3148 if (tmpchar == '#')
3149 {
3150 while (feof (stream) == 0 && tmpchar != '\n')
3151 tmpchar = fgetc (stream);
3152 }
3153
3154 // if (tmpchar>='0' && tmpchar <='9') i++;
3155 // else if (tmpchar==',' || tmpchar=='.') i++;
3156
3157 if ((tmpchar >= '0'
3158 && tmpchar <= '9')
3159 || tmpchar == ',' || tmpchar == '.' || tmpchar == '-')
3160 {
3161 i++;
3162 }
3163 } //end while
3164 //END count file elements
3165
3166 /*
3167 //20070116 DECIDED NOT TO USE THIS METHOD:
3168 //Now init SG so that it can start to receive the schedule.
3169 //Since this is an old Gnaural file, there must be only two voices, first being
3170 //the assumed noise, the second being the explicitly directed binaural beat. The
3171 //philosophy of Gnaural file opening is to let the Graph (SG) always lead the BB
3172 //engine; and in loading the graph, it is customary to load the SG_UndoRedo buffer
3173 //with the file data, because when using SG_RestoreDataPoints() you only need:
3174 // 1) SG_UndoRedo.count field filled (total count of ALL datapoints)
3175 // 2) SG_UndoRedo.VoiceType allocated (one for each voice) and filled
3176 // 3) SG_UndoRedo.DPdata allotted and these DPdata struct fields filled:
3177 // - duration
3178 // - beatfreq
3179 // - basefreq
3180 // - volume_left
3181 // - volume_right
3182 // - state
3183 // - parent
3184 */
3185
3186 //preparing the holding vars for command line parsing:
3187 OldGnauralVars.FreqBase = 220;
3188 OldGnauralVars.Volume_Tone = .5;
3189 OldGnauralVars.Volume_Noiz = .5;
3190 OldGnauralVars.loops = 1;
3191 OldGnauralVars.loopcount = 1;
3192 //START allocate char buffer, rewind file, and do it again this time putting elements in buffer:
3193 str = (char *) malloc ((++i));
3194 rewind (stream);
3195 i = 0;
3196 while (feof (stream) == 0)
3197 {
3198 tmpchar = fgetc (stream);
3199 //deal with comments:
3200 if (tmpchar == '#')
3201 { //throw whole line away:
3202 while (feof (stream) == 0 && tmpchar != '\n')
3203 tmpchar = fgetc (stream);
3204 }
3205
3206 //deal with command strings:
3207 else if (tmpchar == '[')
3208 {
3209 main_GNAURAL1FILE_ParseCmd (stream);
3210 }
3211
3212 //if it is a number, add it to the monster string:
3213 else
3214 if ((tmpchar >= '0'
3215 && tmpchar <= '9')
3216 || tmpchar == ',' || tmpchar == '.' || tmpchar == '-')
3217 {
3218 str[i] = tmpchar;
3219 i++;
3220 }
3221 }
3222 //END allocate char buffer, rewind file, and do it again this time putting elements in buffer:
3223
3224 fclose (stream);
3225 //put end on the string!!
3226 str[i] = '\0';
3227 main_GNAURAL1FILE_SchedBuffToSchedule (str);
3228 free (str);
3229 return 1;
3230 }
3231
3232 //====END OF IMPORT OLD GNAURAL FILE SECTION====
3233
3234 //======================================
3235 //I bet I could do this without actually writing to disk, but
3236 //have wrestled with so many bizarrely non-functional GTK
3237 //themed/builtin/window/named icon functions that
3238 //after a full day of failing, I must simply do something
3239 //I understand (see icon_problems.txt):
main_SetIcon()3240 void main_SetIcon ()
3241 {
3242 #ifdef GNAURAL_WIN32
3243 // Get the temp path.
3244 if (0 != GetTempPath (sizeof (main_sPathTemp), main_sPathTemp)) //NOTE: returned string ends in backslash
3245 { //success
3246 strcat (main_sPathTemp, "gnaural-icon.png");
3247 }
3248 else
3249 { //fail
3250 sprintf (main_sPathTemp, "gnaural-icon.png");
3251 }
3252 #endif
3253
3254 #ifndef GNAURAL_WIN32
3255 sprintf (main_sPathTemp, "%s/%s", main_sPathGnaural, "gnaural-icon.png");
3256 #endif
3257 FILE *stream;
3258
3259 if ((stream = fopen (main_sPathTemp, "wb")) == NULL)
3260 {
3261 return;
3262 }
3263 fwrite (&main_GnauralIcon, sizeof (main_GnauralIcon), 1, stream);
3264 fclose (stream);
3265 gtk_window_set_default_icon_from_file (main_sPathTemp, NULL);
3266 }
3267
3268 //======================================
main_InterceptCtrl_C(int sig)3269 void main_InterceptCtrl_C (int sig)
3270 {
3271 SG_DBGOUT ("Caught Ctrl-C");
3272 /*
3273 if (gnaural_guiflag == GNAURAL_GUIMODE) {
3274 //20060328: I used to just call gtk_main_quit() here, but it
3275 //is smarter to let the callback functions associated with window1
3276 //do their own cleanup:
3277 gtk_widget_destroy( window1 );
3278 // ScheduleGUI_delete_event
3279 DBGOUT ("Quit GUI mode");
3280 }
3281 */
3282 main_Cleanup ();
3283 exit (EXIT_SUCCESS);
3284 }
3285
3286 //======================================
main_ParseCmdLine(int argc,char * argv[])3287 void main_ParseCmdLine (int argc, char *argv[])
3288 {
3289 opterr = 0; //this means I want to check the ? character myself for errors
3290 int c;
3291
3292 //NOTE: options with ":" after them require arguments
3293 while ((c = getopt (argc, argv, GNAURAL_CMDLINEOPTS)) != -1)
3294 switch (c)
3295 {
3296 case 'h': //print help
3297 fprintf (stdout, "Gnaural (ver. %s) - ", VERSION);
3298 fputs (main_GnauralHelp, stdout);
3299 exit (EXIT_SUCCESS); //completely quit if the user asked for help.
3300 break;
3301 case 'i': //show terminal-style gui info
3302 ++cmdline_i;
3303 break;
3304 case 'p': //output directly to sound system
3305 ++cmdline_p;
3306 break;
3307 case 'd': //output debugging info to stderr
3308 ++cmdline_d;
3309 SG_DebugFlag = TRUE;
3310 BB_DebugFlag = TRUE;
3311 GN_DebugFlag = TRUE;
3312 //may go back in to GUI mode, can't know from this
3313 break;
3314 case 'o': //output a .WAV stream to stdout
3315 ++cmdline_o;
3316 break;
3317 case 's': //create default gnaural_schedule.txt file
3318 ++cmdline_s;
3319 if (0 < cmdline_w)
3320 {
3321 SG_ERROUT ("Ignoring -w; -s has priority");
3322 cmdline_w = 0; //just in case I missed something...
3323 }
3324 break;
3325 case 'a':
3326 ++cmdline_a;
3327 playAudio_SoundDevice = atoi (optarg);
3328 //may go back in to GUI mode, can't know from this
3329 break;
3330 case 'w': //output a .WAV stream to a user specified .wav file
3331 if (cmdline_s > 0)
3332 { //CHANGE 051110: you don't want -s and -w in the same line
3333 SG_ERROUT ("Ignoring -w; -s has priority");
3334 cmdline_w = 0; //just in case I missed something...
3335 break;
3336 }
3337 ++cmdline_w;
3338 //20071201 Following could probably be done better now:
3339 SG_StringAllocateAndCopy (&main_AudioWriteFile_name, optarg);
3340 break;
3341 case ':': //some option was missing its argument
3342 fprintf (stderr, "Missing argument for `-%c'.\n", optopt);
3343 fputs (main_GnauralHelpShort, stderr);
3344 exit (EXIT_FAILURE); //completely quit on error
3345 case '?':
3346 fprintf (stderr, "Unknown option `-%c'.\n", optopt);
3347 fputs (main_GnauralHelpShort, stderr);
3348 exit (EXIT_FAILURE); //completely quit on error
3349 default:
3350 fputs (main_GnauralHelpShort, stderr);
3351 exit (EXIT_FAILURE); //completely quit on error
3352 break;
3353 }
3354
3355 //now see if there was any command-line stuff user inputted that didn't
3356 //get caught above.
3357 //a20070705: The last thing to get here is considered a filename
3358 int index;
3359
3360 for (index = optind; index < argc; index++)
3361 {
3362 //[20100405: added check to avoid compiler complaints]:
3363 sprintf (main_sCurrentGnauralFilenameAndPath, "%s", argv[index]);
3364 // fprintf (stderr, "Error: non-option argument \"%s\"\n", argv[index]);
3365 }
3366 //END command line parsing
3367 return;
3368 }
3369
3370 //======================================
3371 //this is basically just the command line version dropped in as a function-call in
3372 //to the GUI version.
main_RunCmdLineMode()3373 void main_RunCmdLineMode ()
3374 {
3375 //don't need GUI, so turn pause off:
3376 gnaural_pauseflag = 0;
3377 //first do the things that don't require sound at all:
3378 //- cmdline_s: create a fresh gnaural_schedule.txt file then exits DOES NOT REQUIRE SOUND SYSTEM
3379 //- cmdline_o: dump a .WAV file to sdtout DOES NOT REQUIRE SOUND SYSTEM
3380 //- cmdline_w: dump a .WAV file to a file DOES NOT REQUIRE SOUND SYSTEM
3381 // user asked to create a new default Schedule file; do that then exit:
3382 if (cmdline_s > 0)
3383 {
3384 // bb->WriteDefaultScheduleToFile ();
3385 SG_SetupDefaultDataPoints (2);
3386 gxml_XMLWriteFile ("schedule.gnaural");
3387 SG_DBGOUT ("Wrote default gnaural_schedule.txt file");
3388 return;
3389 }
3390
3391 ////////////////
3392 //NOTE:
3393 //from this point forward, I work on the premise that user may be piping
3394 //output to stdout to another program expecting a WAV file
3395 ////////////////
3396
3397 //these next three are mutually exclusive, and only cmdline_p needs sound system:
3398 if (cmdline_w != 0)
3399 {
3400 //old direct way of doing this:
3401 // fprintf (stderr, "Writing schedule %s to file %s... ",
3402 // bb->SchedFilename, w_str);
3403 // bb->WriteWAVFile (w_str);
3404 // fprintf (stderr, "Done.\n");
3405 // return (EXIT_SUCCESS);
3406
3407 //threaded way of doing the above:
3408 if (cmdline_i != 0)
3409 {
3410 fputs ("\033[2J", stderr); //clear entire screen
3411 fputs ("\033[14;1H", stderr); //place cursor
3412 }
3413
3414 fprintf (stderr,
3415 "Writing WAV data to file %s... \n", main_AudioWriteFile_name);
3416 //new gthread code (new 20051126):
3417 GError *thread_err = NULL;
3418
3419 if ((main_AudioWriteFile_thread =
3420 g_thread_create ((GThreadFunc) main_AudioWriteFile, (void *) NULL,
3421 TRUE, &thread_err)) == NULL)
3422 {
3423 fprintf (stderr, "g_thread_create failed: %s!!\n", thread_err->message);
3424 g_error_free (thread_err);
3425 }
3426
3427 while
3428 (main_AudioWriteFile_thread
3429 != NULL && (((BB_InfoFlag) & BB_COMPLETED) == 0))
3430 {
3431 if (cmdline_i != 0)
3432 {
3433 main_UpdateTerminalGUI (stdout);
3434 fflush (stdout); //must flush often when using stdout
3435 }
3436 main_Sleep (G_USEC_PER_SEC);
3437 }
3438 if (cmdline_i != 0)
3439 fputs ("\033[14;1H", stderr); //place cursor
3440 return;
3441 }
3442
3443 //----------
3444
3445 else if (cmdline_o != 0)
3446 {
3447 BB_WriteWAVToStream (stdout);
3448 return;
3449 }
3450
3451 //----------
3452
3453 else if (cmdline_p != 0)
3454 { //write to sound system:
3455 //First, set up the sound system, or exit if not possible:
3456 //first set-up sound, since program may not return from parsing command line
3457 if (EXIT_FAILURE == main_playAudio_SoundInit ())
3458 {
3459 SG_DBGOUT ("Couldn't intialize the sound system, exiting");
3460 Pa_Terminate ();
3461 playAudio_SoundStream = NULL; //this is how the rest of the program knows if we're using sound or not.
3462 return;
3463 }
3464
3465 SG_DBGOUT ("Writing to sound system");
3466 if (cmdline_i != 0)
3467 {
3468 fputs ("\033[2J", stderr); //clear entire screen
3469 fputs ("\033[14;1H", stderr); //place cursor
3470 }
3471
3472 playAudio_SoundStart ();
3473 while (playAudio_SoundStream != NULL
3474 && (((BB_InfoFlag) & BB_COMPLETED) == 0))
3475 {
3476 if (cmdline_i != 0)
3477 {
3478 main_UpdateTerminalGUI (stdout);
3479 fflush (stdout); //must flush often when using stdout
3480 }
3481 Pa_Sleep (1000);
3482 };
3483 if (cmdline_i != 0)
3484 {
3485 fputs ("\033[14;1H", stderr); //place cursor
3486 }
3487 return;
3488 }
3489 }
3490
3491 //======================================
3492 //updates a terminal window via either stdout or stderr streams
main_UpdateTerminalGUI(FILE * gstream)3493 void main_UpdateTerminalGUI (FILE * gstream)
3494 {
3495 //NOTE THIS IS CURRENTLY MOSTLY UNIMPLEMENTED; a lot of decisions
3496 //will need to be made how to present multi voiced data without a GUI
3497 // current time point within schedule:
3498
3499 main_FormatProgressString ();
3500 fputs ("\033[2;1H", gstream); //place cursor
3501 fprintf (gstream, "%s", main_sTmp);
3502 fputs ("\033[K", gstream); //clear to end of line
3503 /*
3504 //-----------------------------------------------------------
3505 //START stuff that gets updated EVERY second:
3506 //
3507 // current time point within schedule:
3508 fputs("\033[2;1H", gstream); //place cursor
3509 fprintf (gstream, "Current Progress: \t%d min. \t%d sec.",
3510 (BB_CurrentSampleCount + BB_CurrentSampleCountLooped) / 6000,
3511 ((BB_CurrentSampleCount + BB_CurrentSampleCountLooped) / 100) % 60);
3512 fputs("\033[K", gstream); //clear to end of line
3513
3514 //remaining time in entry:
3515 fputs("\033[5;1H", gstream); //place cursor
3516 fprintf (gstream, "Time left: \t\t%d sec.",
3517 ((bb->Schedule[bb->ScheduleCount].AbsoluteEndTime_100 - BB_CurrentSampleCount) / 100));
3518 fputs("\033[K", gstream); //clear to end of line
3519
3520 //Left frequency:
3521 fputs("\033[9;1H", gstream); //place cursor
3522 fprintf (gstream, "Current Freq. Left: \t%g\n",
3523 bb->FreqL);
3524 fputs("\033[K", gstream); //clear to end of line
3525
3526 //Right frequency:
3527 // fputs("\033[15;1H",gstream);//place cursor
3528 fprintf (gstream, "Current Freq. Right: \t%g\n",
3529 bb->FreqR);
3530 fputs("\033[K", gstream); //clear to end of line
3531
3532 //Beat frequency:
3533 // fputs("\033[16;1H",gstream);//place cursor
3534 fprintf (gstream, "Current Beat Freq.: \t%g\n",
3535 bb->FreqL - bb->FreqR);
3536 fputs("\033[K", gstream); //clear to end of line
3537
3538 //
3539 //END stuff that gets updated EVERY second:
3540 //-----------------------------------------------------------------------
3541
3542 //-----------------------------------------------------------------------
3543 //START check bb->InfoFlag
3544 //now update things that BinauralBeat says are changed:
3545 if (bb->InfoFlag != 0) {
3546
3547 //.........................
3548 //if schedule is done:
3549 if (((bb->InfoFlag) & BB_COMPLETED) != 0) {
3550 fputs("\033[7;1H", gstream); //place cursor
3551 fprintf (gstream, "Schedule Completed");
3552 fputs("\033[K", gstream); //clear to end of line
3553 }
3554
3555 //.........................
3556 //if starting a new loop:
3557 if (((bb->InfoFlag) & BB_NEWLOOP) != 0) {
3558 //reset the "new loop" bit of InfoFlag:
3559 bb->InfoFlag &= ~BB_NEWLOOP;
3560 //bbl_UpdateGUI_LoopInfo ();
3561 }
3562
3563 //.........................
3564 //START things that get updated every new entry:
3565 //if new entry
3566 if (((bb->InfoFlag) & BB_NEWENTRY) != 0) {
3567 //reset the "new entry" bit of InfoFlag:
3568 (bb->InfoFlag) &= (~BB_NEWENTRY);
3569
3570 //current entry number:
3571 fputs("\033[3;1H", gstream); //place cursor
3572 fprintf (gstream, "Current Entry: \t\t%d of %d",
3573 bb->ScheduleCount + 1, bb->ScheduleEntriesCount);
3574 fputs("\033[K", gstream); //clear to end of line
3575
3576 //total time in entry:
3577 fputs("\033[4;1H", gstream); //place cursor
3578 fprintf (gstream, "Duration: \t\t%g sec.",
3579 bb->Schedule[bb->ScheduleCount].Duration);
3580 fputs("\033[K", gstream); //clear to end of line
3581
3582 //fprintf(gstream,"%d",bb->Schedule[bb->ScheduleCount].AbsoluteEndTime_100/100);
3583 //SetDlgItemText(lEndCount, gstream);
3584
3585 fputs("\033[6;1H", gstream); //place cursor
3586 fprintf (gstream, "Beat Range: \t\t%g to %g Hz\n",
3587 bb->Schedule[bb->ScheduleCount].FreqLStart -
3588 bb->Schedule[bb->ScheduleCount].FreqRStart,
3589 bb->Schedule[bb->ScheduleCount].FreqLEnd -
3590 bb->Schedule[bb->ScheduleCount].FreqREnd);
3591 fputs("\033[K", gstream); //clear to end of line
3592
3593 //left
3594 // fputs("\033[7;1H",gstream);//place cursor
3595 fprintf (gstream, "Left ear: \tStart:\t%g",
3596 bb->Schedule[bb->ScheduleCount].FreqLStart);
3597 fputs("\033[K", gstream); //clear to end of line
3598 fprintf (gstream, "\tEnd:\t%g\n",
3599 bb->Schedule[bb->ScheduleCount].FreqLEnd);
3600 fputs("\033[K", gstream); //clear to end of line
3601
3602 //right
3603 // fputs("\033[8;1H",gstream);//place cursor
3604 fprintf (gstream, "Right ear: \tStart:\t%g",
3605 bb->Schedule[bb->ScheduleCount].FreqRStart);
3606 fputs("\033[K", gstream); //clear to end of line
3607 fprintf (gstream, "\tEnd:\t%g",
3608 bb->Schedule[bb->ScheduleCount].FreqREnd);
3609 fputs("\033[K", gstream); //clear to end of line
3610
3611 //this is overdoing it, but need it updated sometime:
3612
3613 if (bb->loops > 0) {
3614 fputs("\033[1;1H", gstream); //place cursor
3615 fprintf (gstream, "Projected Runtime: \t%d min. \t%d sec.",
3616 (int) (bb->loops * bb->ScheduleTime) / 60,
3617 (int) (bb->loops * bb->ScheduleTime) % 60);
3618 fputs("\033[K", gstream); //clear to end of line
3619 } else {
3620 fputs("\033[1;1H", gstream); //place cursor
3621 fprintf (gstream, "Projected Runtime: FOREVER (Inf. Loop Mode)");
3622 fputs("\033[K", gstream); //clear to end of line
3623 }
3624
3625 }
3626 //END things that get updated every new entry
3627 }
3628 */
3629 }
3630
3631 //======================================
main_WriteDefaultFile()3632 void main_WriteDefaultFile ()
3633 {
3634 //20070705: Used to write to whatever was in main_sCurrentOpenGnauralFile,
3635 //but now sets-up default names and paths first:
3636 main_sCurrentGnauralFilenameAndPath[0] = '\0'; //not necessary, I guess
3637 main_SetupPathsAndFiles (TRUE);
3638 //prepare file access:
3639 FILE *stream;
3640
3641 if ((stream = fopen (main_sCurrentGnauralFilenameAndPath, "w")) == NULL)
3642 {
3643 SG_ERROUT ("Failed to open file for writing!");
3644 return;
3645 }
3646 fprintf (stream, "%s", main_DefaultSchedule);
3647 fclose (stream);
3648 }
3649
3650 /////////////////////////////////////////////////////
3651 //20100610
main_VoiceProperties_checkbuttonMono(GtkWidget * widget,gpointer Voice)3652 void main_VoiceProperties_checkbuttonMono (GtkWidget * widget, gpointer Voice)
3653 {
3654 SG_Voice *curVoice = (SG_Voice *) Voice;
3655 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
3656 {
3657 curVoice->mono = TRUE; //user wants voice mono
3658 }
3659 else
3660 {
3661 curVoice->mono = FALSE; //user wants voice mono
3662 }
3663 }
3664
3665 /////////////////////////////////////////////////////
3666 //20070627: This edits properties of an existing
3667 //voices, OR creates a new voice if curVoice = NULL
main_VoicePropertiesDialog(GtkWidget * widget,SG_Voice * curVoice)3668 gboolean main_VoicePropertiesDialog (GtkWidget * widget, SG_Voice * curVoice)
3669 {
3670 GtkWidget *dialog = gtk_dialog_new_with_buttons ("Voice Properties", NULL,
3671 (GtkDialogFlags)
3672 (GTK_DIALOG_MODAL |
3673 GTK_DIALOG_DESTROY_WITH_PARENT),
3674 GTK_STOCK_OK,
3675 GTK_RESPONSE_OK,
3676 GTK_STOCK_CANCEL,
3677 GTK_RESPONSE_CANCEL,
3678 "Choose Audio File",
3679 GTK_RESPONSE_APPLY,
3680 "Delete Voice",
3681 GTK_RESPONSE_NO, NULL);
3682
3683 //add some stuff:
3684 GtkWidget *label_VoiceType = gtk_label_new ("Voice Type:");
3685 GtkWidget *comboboxentry1 = gtk_combo_box_entry_new_text ();
3686 GtkWidget *checkbuttonMono = gtk_check_button_new_with_label ("Monophonic");
3687 gtk_widget_show (comboboxentry1);
3688 //e20070621: List order: don't change; depends on #define values in BinauralBeat.h:
3689 gtk_combo_box_append_text (GTK_COMBO_BOX (comboboxentry1),
3690 main_sBinauralBeat);
3691 gtk_combo_box_append_text (GTK_COMBO_BOX (comboboxentry1), main_sPinkNoise);
3692 gtk_combo_box_append_text (GTK_COMBO_BOX (comboboxentry1), main_sAudioFile);
3693 gtk_combo_box_append_text (GTK_COMBO_BOX (comboboxentry1),
3694 main_sIsochronicPulses);
3695 gtk_combo_box_append_text (GTK_COMBO_BOX (comboboxentry1),
3696 main_sIsochronicPulses_alt);
3697 gtk_combo_box_append_text (GTK_COMBO_BOX (comboboxentry1), main_sWaterDrops);
3698 gtk_combo_box_append_text (GTK_COMBO_BOX (comboboxentry1), main_sRain);
3699
3700 GtkWidget *label_VoiceDescrip =
3701 gtk_label_new ("Voice Description [or Filename if Audio File]:");
3702 GtkWidget *entry_Input_VoiceDesrip = gtk_entry_new ();
3703
3704 //put stuff in here relevant to a pre-existing voice's state (as
3705 //opposed to a request for new voice):
3706 if (curVoice != NULL)
3707 {
3708 gtk_entry_set_text ((GtkEntry
3709 *) entry_Input_VoiceDesrip, curVoice->description);
3710 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbuttonMono),
3711 curVoice->mono);
3712 g_signal_connect (checkbuttonMono, "clicked",
3713 G_CALLBACK (main_VoiceProperties_checkbuttonMono),
3714 (gpointer) curVoice);
3715 }
3716
3717 gtk_combo_box_set_active ((GtkComboBox *) comboboxentry1,
3718 (curVoice != NULL) ? curVoice->type : 0);
3719 // Add the label, and show everything we've added to the dialog.
3720 gtk_container_add
3721 (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label_VoiceType);
3722 gtk_container_add
3723 (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), comboboxentry1);
3724 gtk_container_add
3725 (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label_VoiceDescrip);
3726 gtk_container_add
3727 (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), entry_Input_VoiceDesrip);
3728 gtk_container_add
3729 (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), checkbuttonMono);
3730 gtk_widget_show_all (dialog);
3731 gint res = FALSE;
3732
3733 do
3734 {
3735 //block until I get a response:
3736 res = gtk_dialog_run (GTK_DIALOG (dialog));
3737 switch (res)
3738 {
3739 case GTK_RESPONSE_OK:
3740 {
3741 if (curVoice != NULL)
3742 {
3743 curVoice->type =
3744 gtk_combo_box_get_active ((GtkComboBox *) comboboxentry1);
3745 }
3746 else
3747 { //it is a brand new voice:
3748 curVoice =
3749 SG_AddNewVoiceToEnd (gtk_combo_box_get_active
3750 ((GtkComboBox *) comboboxentry1), 0);
3751
3752 //start 110520
3753 //purpose: to give sensible defaults for new voices because
3754 //always must manually add a first data point to the end of
3755 //a new voice:
3756 switch (curVoice->type)
3757 {
3758
3759 case BB_VOICETYPE_BINAURALBEAT:
3760 case BB_VOICETYPE_ISOPULSE:
3761 case BB_VOICETYPE_ISOPULSE_ALT:
3762 SG_AddNewDataPointToEnd (curVoice->FirstDataPoint, curVoice, SG_TotalScheduleDuration, //duration
3763 4, //beatfreq
3764 .5, //volume
3765 .5, //volume
3766 180, //basefreq
3767 SG_SELECTED);
3768 break;
3769
3770 case BB_VOICETYPE_PINKNOISE:
3771 SG_AddNewDataPointToEnd (curVoice->FirstDataPoint, curVoice, SG_TotalScheduleDuration, //duration
3772 0, //beatfreq
3773 .1, //volume
3774 .1, //volume
3775 0, //basefreq
3776 SG_SELECTED);
3777 break;
3778
3779 case BB_VOICETYPE_PCM:
3780 SG_AddNewDataPointToEnd (curVoice->FirstDataPoint, curVoice, SG_TotalScheduleDuration, //duration
3781 0, //beatfreq
3782 .5, //volume
3783 .5, //volume
3784 0, //basefreq
3785 SG_SELECTED);
3786 break;
3787
3788 case BB_VOICETYPE_WATERDROPS:
3789 SG_AddNewDataPointToEnd (curVoice->FirstDataPoint, curVoice, SG_TotalScheduleDuration, //duration
3790 2, //beatfreq (number of drops)
3791 .5, //volume
3792 .5, //volume
3793 0.000352858, //basefreq (probability)
3794 SG_SELECTED);
3795 break;
3796
3797 case BB_VOICETYPE_RAIN:
3798 SG_AddNewDataPointToEnd (curVoice->FirstDataPoint, curVoice, SG_TotalScheduleDuration, //duration
3799 8, //beatfreq
3800 .5, //volume
3801 .5, //volume
3802 .1, //basefreq
3803 SG_SELECTED);
3804 break;
3805
3806 default:
3807 SG_AddNewDataPointToEnd (curVoice->FirstDataPoint, curVoice, SG_TotalScheduleDuration, //duration
3808 0, //beatfreq
3809 .5, //volume
3810 .5, //volume
3811 0, //basefreq
3812 SG_SELECTED);
3813 break;
3814 }
3815
3816 //end 110520
3817 }
3818
3819 SG_StringAllocateAndCopy (&
3820 (curVoice->description),
3821 (char
3822 *)
3823 gtk_entry_get_text
3824 (GTK_ENTRY (entry_Input_VoiceDesrip)));
3825 SG_SelectVoice (curVoice);
3826 // SG_DeselectDataPoints ();
3827 // SG_SelectDataPoints_Voice (curVoice, TRUE);
3828 // SG_ConvertDataToXY (widget);
3829 SG_DrawGraph (widget);
3830 // VG_DestroyTreeView ();//20100625
3831 main_UpdateGUI_Voices (main_vboxVoices);
3832 SG_GraphHasChanged = SG_DataHasChanged = TRUE; //datahaschanged because voicetype may have changed locally
3833 }
3834 break;
3835 //pick audio file:
3836 case GTK_RESPONSE_APPLY:
3837 {
3838 //old way:
3839 //gchar *tmpfilename =
3840 //main_OpenFileDialog ("~Gnaural Audio Files~*.wav,*.aiff,*.au,*.flac,~All Files~*");
3841 //new way:
3842 main_MakeAudioFormatsFileFilterString (main_sTmp, sizeof (main_sTmp));
3843 gchar *tmpfilename = main_OpenFileDialog (main_sTmp, main_sPathGnaural);
3844
3845 if (NULL != tmpfilename)
3846 {
3847 gtk_entry_set_text ((GtkEntry *) entry_Input_VoiceDesrip, tmpfilename);
3848 }
3849 g_free (tmpfilename);
3850 }
3851 break;
3852 //delete voice:
3853 case GTK_RESPONSE_NO:
3854 main_DeleteSelectedVoice (widget);
3855 break;
3856 default:
3857 res = FALSE;
3858 break;
3859 }
3860 }
3861 while (res == GTK_RESPONSE_APPLY);
3862 gtk_widget_destroy (dialog);
3863 return res;
3864 }
3865
3866 /////////////////////////////////////////////////////
main_on_revert_activate(GtkMenuItem * menuitem,gpointer user_data)3867 void main_on_revert_activate (GtkMenuItem * menuitem, gpointer user_data)
3868 {
3869 if (0 != main_TestPathOrFileExistence (main_sCurrentGnauralFilenameAndPath))
3870 {
3871 SG_ERROUT ("Can't revert to a file that doesn't exist.");
3872 return;
3873 }
3874 gxml_XMLReadFile
3875 (main_sCurrentGnauralFilenameAndPath, main_drawing_area, FALSE);
3876 }
3877
3878 /////////////////////////////////////////////////////
3879 //erases all voices and leaves user with one voice with one DP
main_NewGraph(int voices)3880 void main_NewGraph (int voices)
3881 {
3882 if (FALSE == main_SetScheduleInfo (FALSE))
3883 {
3884 return;
3885 }
3886 SG_SetupDefaultDataPoints (voices);
3887 main_SetLoops (1);
3888 //SG_DrawGraph (main_drawing_area);
3889 SG_GraphHasChanged = SG_DataHasChanged = TRUE; //20070105 tricky way to do main_LoadBinauralBeatSoundEngine in a bulk way
3890 sprintf (main_sTmp, "%s/untitled.gnaural", main_sPathGnaural);
3891 main_UpdateGUI_FileInfo (main_sTmp);
3892 VG_DestroyTreeView (); //20100624
3893 main_UpdateGUI_Voices (main_vboxVoices);
3894 //this is really just a way for user to free up big chunks of memory if they had lots of AFs open:
3895 main_CleanupAudioFileData ();
3896 }
3897
3898 /////////////////////////////////////////////////////
main_UpdateGUI_FileInfo(char * filename)3899 void main_UpdateGUI_FileInfo (char *filename)
3900 {
3901 strcpy (main_sCurrentGnauralFilenameAndPath, filename);
3902 sprintf (main_sTmp, "File: %s", main_sCurrentGnauralFilenameAndPath);
3903 gtk_label_set_text (main_labelFileName, main_sTmp);
3904 sprintf (main_sTmp, "Author: %s", main_Info_Author);
3905 gtk_label_set_text (main_labelFileAuthor, main_sTmp);
3906 sprintf (main_sTmp, "Description: %s", main_Info_Description);
3907 gtk_label_set_text (main_labelFileDescription, main_sTmp);
3908 sprintf (main_sTmp, "Title: %s", main_Info_Title);
3909 gtk_label_set_text (main_labelFileTitle, main_sTmp);
3910 }
3911
3912 /////////////////////////////////////////////////////
3913 //clears all but first DP:
main_ClearDataPointsInVoice(SG_Voice * curVoice)3914 void main_ClearDataPointsInVoice (SG_Voice * curVoice)
3915 {
3916 if (curVoice == NULL)
3917 {
3918 return;
3919 }
3920 SG_DeselectDataPoints ();
3921 SG_SelectDataPoints_Voice (curVoice, TRUE);
3922 curVoice->FirstDataPoint->state = SG_UNSELECTED;
3923 SG_DeleteDataPoints (main_drawing_area, TRUE, TRUE);
3924 }
3925
3926 /////////////////////////////////////////////////////
3927 //clears all but first DP in ALL voices:
main_ClearDataPointsInVoices()3928 void main_ClearDataPointsInVoices ()
3929 {
3930 SG_BackupDataPoints (main_drawing_area);
3931 SG_Voice *curVoice = SG_FirstVoice;
3932
3933 while (curVoice != NULL)
3934 {
3935 main_ClearDataPointsInVoice (curVoice);
3936 curVoice = curVoice->NextVoice;
3937 }
3938 }
3939
3940 ///////////////////////////////////////////
3941 //returns TRUE if user clicked OK, filling val with result
main_AskUserForNumberDialog(char * title,char * question,double * startingval)3942 double main_AskUserForNumberDialog
3943 (char *title, char *question, double *startingval)
3944 {
3945 GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
3946 (GtkWindow *) main_window,
3947 (GtkDialogFlags)
3948 (GTK_DIALOG_MODAL |
3949 GTK_DIALOG_DESTROY_WITH_PARENT),
3950 GTK_STOCK_OK,
3951 GTK_RESPONSE_ACCEPT,
3952 GTK_STOCK_CANCEL,
3953 GTK_RESPONSE_REJECT, NULL);
3954
3955 gboolean res = FALSE;
3956
3957 //add some stuff:
3958 GtkWidget *label = gtk_label_new (question);
3959
3960 GtkWidget *entry_Input = gtk_entry_new ();
3961
3962 sprintf (main_sTmp, "%g", *startingval);
3963 gtk_entry_set_text (GTK_ENTRY (entry_Input), main_sTmp);
3964 // Add the label, and show everything we've added to the dialog.
3965 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label);
3966 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), entry_Input);
3967 gtk_widget_show_all (dialog);
3968 //block until I get a response:
3969 gint result = gtk_dialog_run (GTK_DIALOG (dialog));
3970
3971 switch (result)
3972 {
3973 case GTK_RESPONSE_ACCEPT:
3974 {
3975 *startingval = atof (gtk_entry_get_text (GTK_ENTRY (entry_Input)));
3976 res = TRUE;
3977 }
3978 break;
3979 default:
3980 break;
3981 }
3982 gtk_widget_destroy (dialog);
3983 return res;
3984 }
3985
3986 ///////////////////////////////////////////
main_SetScheduleInfo(gboolean FillEntriesFlag)3987 gboolean main_SetScheduleInfo (gboolean FillEntriesFlag)
3988 {
3989 gboolean ok = FALSE;
3990
3991 GtkWidget *dialog = gtk_dialog_new_with_buttons ("Edit Schedule Info",
3992 (GtkWindow *) main_window,
3993 (GtkDialogFlags)
3994 (GTK_DIALOG_MODAL |
3995 GTK_DIALOG_DESTROY_WITH_PARENT),
3996 GTK_STOCK_OK,
3997 GTK_RESPONSE_ACCEPT,
3998 GTK_STOCK_CANCEL,
3999 GTK_RESPONSE_REJECT, NULL);
4000
4001 //add some stuff:
4002 GtkWidget *label_title = gtk_label_new ("Title:");
4003
4004 GtkWidget *entry_Input_title = gtk_entry_new ();
4005
4006 if (TRUE == FillEntriesFlag)
4007 {
4008 gtk_entry_set_text (GTK_ENTRY (entry_Input_title), main_Info_Title);
4009 }
4010 GtkWidget *label_description = gtk_label_new ("Description");
4011
4012 GtkWidget *entry_Input_description = gtk_entry_new ();
4013
4014 if (TRUE == FillEntriesFlag)
4015 {
4016 gtk_entry_set_text (GTK_ENTRY
4017 (entry_Input_description), main_Info_Description);
4018 }
4019 GtkWidget *label_author = gtk_label_new ("Author:");
4020
4021 GtkWidget *entry_Input_author = gtk_entry_new ();
4022
4023 if (TRUE == FillEntriesFlag)
4024 {
4025 gtk_entry_set_text (GTK_ENTRY (entry_Input_author), main_Info_Author);
4026 }
4027 // Add the label, and show everything we've added to the dialog.
4028 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label_title);
4029 gtk_container_add
4030 (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), entry_Input_title);
4031 gtk_container_add
4032 (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label_description);
4033 gtk_container_add
4034 (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), entry_Input_description);
4035 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label_author);
4036 gtk_container_add
4037 (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), entry_Input_author);
4038 gtk_widget_show_all (dialog);
4039 //block until I get a response:
4040 gint result = gtk_dialog_run (GTK_DIALOG (dialog));
4041
4042 switch (result)
4043 {
4044 case GTK_RESPONSE_ACCEPT:
4045 {
4046 ok = TRUE;
4047 SG_StringAllocateAndCopy
4048 (&main_Info_Title, gtk_entry_get_text (GTK_ENTRY (entry_Input_title)));
4049 SG_StringAllocateAndCopy
4050 (&main_Info_Description,
4051 gtk_entry_get_text (GTK_ENTRY (entry_Input_description)));
4052 SG_StringAllocateAndCopy
4053 (&main_Info_Author, gtk_entry_get_text (GTK_ENTRY (entry_Input_author)));
4054 main_UpdateGUI_FileInfo (main_sCurrentGnauralFilenameAndPath);
4055 }
4056 break;
4057 default:
4058 break;
4059 }
4060 gtk_widget_destroy (dialog);
4061 return ok;
4062 }
4063
4064 ///////////////////////////////////
main_ReverseVoice()4065 void main_ReverseVoice ()
4066 {
4067 SG_BackupDataPoints (main_drawing_area);
4068 SG_ReverseVoice (main_drawing_area);
4069 }
4070
4071 ///////////////////////////////////
main_DuplicateSelectedVoice()4072 void main_DuplicateSelectedVoice ()
4073 {
4074 SG_BackupDataPoints (main_drawing_area);
4075 SG_DuplicateSelectedVoice ();
4076 //VG_DestroyTreeView ();
4077 main_UpdateGUI_Voices (main_vboxVoices);
4078 }
4079
4080 ///////////////////////////////////
main_ScaleDataPoints_Time(GtkWidget * widget)4081 void main_ScaleDataPoints_Time (GtkWidget * widget)
4082 {
4083 static double scalar = 1.0;
4084
4085 if (TRUE ==
4086 main_AskUserForNumberDialog
4087 ("Time Scale",
4088 "Number to scale duration of selected points by:", &scalar))
4089 {
4090 SG_BackupDataPoints (widget);
4091 SG_ScaleDataPoints_Time (widget, scalar);
4092 }
4093 }
4094
4095 ///////////////////////////////////
main_DeleteSelectedVoice(GtkWidget * widget)4096 void main_DeleteSelectedVoice (GtkWidget * widget)
4097 {
4098 //first check if there was a legally selected voice:
4099 SG_Voice *curVoice = SG_SelectVoice (NULL);
4100
4101 if (curVoice != NULL)
4102 {
4103 SG_BackupDataPoints (widget);
4104 SG_DeleteVoice (curVoice); //NOTE: Might want to base deletion on SG_Voice ID instead...
4105 SG_SelectVoice (SG_FirstVoice);
4106 SG_DrawGraph (widget);
4107 SG_GraphHasChanged = TRUE;
4108 //VG_DestroyTreeView ();
4109 main_UpdateGUI_Voices (main_vboxVoices);
4110 }
4111 }
4112
4113 ///////////////////////////////////
main_ScaleDatPoints_Y(GtkWidget * widget)4114 void main_ScaleDatPoints_Y (GtkWidget * widget)
4115 {
4116 static double scalar = 1.0;
4117
4118 if (TRUE ==
4119 main_AskUserForNumberDialog
4120 ("Scale Y", "Number to scale Y values of selected points by:", &scalar))
4121 {
4122 SG_BackupDataPoints (widget);
4123 SG_ScaleDataPoints_Y (widget, scalar);
4124 }
4125 }
4126
4127 ///////////////////////////////////
main_AddRandomToDataPoints_Y(GtkWidget * widget)4128 void main_AddRandomToDataPoints_Y (GtkWidget * widget)
4129 {
4130 static double val = 0;
4131
4132 if (TRUE ==
4133 main_AskUserForNumberDialog
4134 ("Add Randomness to Y",
4135 "+/- range of random number to add to selected points:", &val))
4136 {
4137 SG_BackupDataPoints (widget);
4138 SG_AddToDataPoints (widget, val, FALSE, TRUE, TRUE);
4139 }
4140 }
4141
4142 ///////////////////////////////////
main_AddRandomToDataPoints_time(GtkWidget * widget)4143 void main_AddRandomToDataPoints_time (GtkWidget * widget)
4144 {
4145 static double val = 0;
4146
4147 if (TRUE ==
4148 main_AskUserForNumberDialog
4149 ("Add Randomness to Durations",
4150 "+/- range of seconds to add to selected points:", &val))
4151 {
4152 SG_BackupDataPoints (widget);
4153 SG_AddToDataPoints (widget, val, TRUE, FALSE, TRUE);
4154 }
4155 }
4156
4157 ///////////////////////////////////
main_AddToDataPoints_Y(GtkWidget * widget)4158 void main_AddToDataPoints_Y (GtkWidget * widget)
4159 {
4160 static double val = 0;
4161
4162 if (TRUE ==
4163 main_AskUserForNumberDialog
4164 ("Add to Y",
4165 "Number of pixels to add to Y axis of selected points:", &val))
4166 {
4167 SG_BackupDataPoints (widget);
4168 SG_AddToDataPoints (widget, val, FALSE, TRUE, FALSE);
4169 }
4170 }
4171
4172 ///////////////////////////////////
main_AddToDataPoints_time(GtkWidget * widget)4173 void main_AddToDataPoints_time (GtkWidget * widget)
4174 {
4175 static double val = 0;
4176
4177 if (TRUE ==
4178 main_AskUserForNumberDialog
4179 ("Add to Durations",
4180 "Value in seconds to add to selected points:", &val))
4181 {
4182 SG_BackupDataPoints (widget);
4183 SG_AddToDataPoints (widget, val, TRUE, FALSE, FALSE);
4184 }
4185 }
4186
4187 ///////////////////////////////////
main_SelectInterval()4188 void main_SelectInterval ()
4189 {
4190 static double val = 2;
4191
4192 if (TRUE ==
4193 main_AskUserForNumberDialog
4194 ("Select Interval", "Select at this interval:", &val))
4195 {
4196 SG_BackupDataPoints (main_drawing_area);
4197 SG_SelectIntervalDataPoints_All ((int) val, TRUE, FALSE);
4198 }
4199 }
4200
4201 ///////////////////////////////////
main_SelectNeighbor()4202 void main_SelectNeighbor ()
4203 { //Select neighbor to right:
4204 static double val = 0;
4205
4206 if (TRUE ==
4207 main_AskUserForNumberDialog
4208 ("Select Next Datapoints",
4209 "Deselect currently selected? (1=yes, 0=no):", &val))
4210 {
4211 SG_BackupDataPoints (main_drawing_area);
4212 SG_SelectNeighboringDataPoints (TRUE, ((val == 1) ? TRUE : FALSE));
4213 }
4214 }
4215
4216 ///////////////////////////////////
main_OpenFile(gboolean OpenMerge,char * path)4217 void main_OpenFile (gboolean OpenMerge, char *path)
4218 { //Open an XML data file:
4219 if (NULL == path)
4220 {
4221 path = main_sPathGnaural;
4222 }
4223 gchar *tmpfilename = main_OpenFileDialog (GNAURAL_FILEFILTERSTRING, path);
4224
4225 if (tmpfilename == NULL)
4226 {
4227 SG_DBGOUT ("Not opening a file.");
4228 return;
4229 }
4230 gxml_XMLReadFile (tmpfilename, main_drawing_area, OpenMerge);
4231 g_free (tmpfilename);
4232 }
4233
4234 ///////////////////////////////////
main_OpenFromLibrary()4235 void main_OpenFromLibrary ()
4236 {
4237 main_OpenFile (FALSE, GNAURAL_PRESET_DIR);
4238 }
4239
4240 ///////////////////////////////////
main_SelectLastDPs()4241 void main_SelectLastDPs ()
4242 {
4243 SG_BackupDataPoints (main_drawing_area);
4244 SG_SelectLastDP_All ();
4245 }
4246
4247 ///////////////////////////////////
main_SelectFirstDPs()4248 void main_SelectFirstDPs ()
4249 {
4250 SG_BackupDataPoints (main_drawing_area);
4251 SG_SelectFirstDP_All ();
4252 }
4253
4254 ///////////////////////////////////
main_TruncateSchedule()4255 void main_TruncateSchedule ()
4256 {
4257 static double val = 60;
4258
4259 if (TRUE ==
4260 main_AskUserForNumberDialog
4261 ("Truncate (or Grow) Schedule",
4262 "Schedule End Time (sec.):", &val) && val >= 0)
4263 {
4264 SG_BackupDataPoints (main_drawing_area);
4265 BB_ResetAllVoices ();
4266 SG_TruncateSchedule (main_drawing_area, val);
4267 }
4268 }
4269
4270 ///////////////////////////////////
main_PasteAtEnd()4271 void main_PasteAtEnd ()
4272 {
4273 SG_BackupDataPoints (main_drawing_area);
4274 SG_PasteDataPointsAtEnd (main_drawing_area);
4275 }
4276
4277 ///////////////////////////////////
main_InvertY()4278 void main_InvertY ()
4279 {
4280 SG_BackupDataPoints (main_drawing_area);
4281 SG_InvertY (main_drawing_area);
4282 }
4283
4284 ///////////////////////////////////
main_SelectDuration()4285 void main_SelectDuration ()
4286 {
4287 static double val = 1;
4288
4289 if (TRUE ==
4290 main_AskUserForNumberDialog
4291 ("Select by Duration (sec.)",
4292 "Selection Threshold (positive for above, negative for below):", &val))
4293 {
4294 SG_BackupDataPoints (main_drawing_area);
4295 SG_SelectDuration (main_drawing_area, val);
4296 }
4297 }
4298
4299 ///////////////////////////////////
main_SelectProximity_All()4300 void main_SelectProximity_All ()
4301 {
4302 static double val = 1;
4303
4304 if (TRUE ==
4305 main_AskUserForNumberDialog
4306 ("Select by X,Y Proximity (pixels)",
4307 "Proximity Threshold:", &val) && val >= 0)
4308 {
4309 SG_BackupDataPoints (main_drawing_area);
4310 SG_SelectProximity_All (val);
4311 // SG_SelectProximity_SingleDP(SG_FirstVoice->FirstDataPoint, val);
4312 }
4313 }
4314
4315 ///////////////////////////////////
main_SelectProximity_SinglePoint()4316 void main_SelectProximity_SinglePoint ()
4317 {
4318 static double val = 1;
4319
4320 if (TRUE ==
4321 main_AskUserForNumberDialog
4322 ("Select by X,Y Proximity (pixels)",
4323 "Proximity Threshold:", &val) && val >= 0)
4324 {
4325 SG_DataPoint *curDP = NULL;
4326
4327 SG_Voice *curVoice = SG_FirstVoice;
4328
4329 while (curVoice != NULL)
4330 {
4331 if (curVoice->hide == FALSE)
4332 {
4333 curDP = curVoice->FirstDataPoint;
4334 do
4335 {
4336 if (SG_SELECTED == curDP->state)
4337 {
4338 break;
4339 }
4340 curDP = curDP->NextDataPoint;
4341 }
4342 while (curDP != NULL);
4343 }
4344 if (curDP != NULL)
4345 {
4346 break;
4347 }
4348 curVoice = curVoice->NextVoice;
4349 }
4350
4351 if (curDP != NULL && SG_SELECTED == curDP->state)
4352 {
4353 SG_BackupDataPoints (main_drawing_area);
4354 SG_SelectProximity_SingleDP (curDP, val);
4355 }
4356 }
4357 }
4358
4359 ///////////////////////////////////
4360 //just a short multiplatform nap; but Sleep is a wonderfully convluted
4361 //occupation. Look what you might do: clock, delay, ftime, gettimeofday,
4362 //msleep, nap, napms, nanosleep, setitimer, sleep, Sleep, times, usleep
4363 //Note the handy G_USEC_PER_SEC
main_Sleep(int microseconds)4364 void main_Sleep (int microseconds)
4365 {
4366 g_usleep (microseconds);
4367 /*
4368 #ifdef GNAURAL_WIN32
4369 SG_DBGOUT_INT ("[Win32] Sleep(ms):", ms);
4370 Sleep (ms);
4371 #endif
4372
4373 #ifndef GNAURAL_WIN32
4374 SG_DBGOUT_INT ("[POSIX] usleep(ms):", ms);
4375 //Obsolete, but using nanosleep feels like using a bazooka for a fly:
4376 usleep (ms);
4377 #endif
4378 */
4379 }
4380
4381 ///////////////////////////////////
main_DuplicateAllVoices()4382 void main_DuplicateAllVoices ()
4383 {
4384 SG_BackupDataPoints (main_drawing_area);
4385 SG_RestoreDataPoints (main_drawing_area, TRUE);
4386 VG_RelistFlag = TRUE;
4387 }
4388
4389 ///////////////////////////////////
4390 //pass this an empty buffer, and it fills it with sound from filename
4391 //and fills size with number of elements in the array of shorts.
4392 //returns 0 for success.
main_LoadSoundFile_shorts(char * filename,short ** buffer,unsigned int * size)4393 int main_LoadSoundFile_shorts
4394 (char *filename, short **buffer, unsigned int *size)
4395 {
4396 SNDFILE *sndfile;
4397
4398 SF_INFO sfinfo;
4399
4400 if (0 != main_CheckSndfileVersion ())
4401 {
4402 return 1;
4403 }
4404
4405 (*buffer) = NULL;
4406 memset (&sfinfo, 0, sizeof (sfinfo));
4407 SG_DBGOUT_STR ("Loading PCM File:", filename);
4408 if (!(sndfile = sf_open (filename, SFM_READ, &sfinfo)))
4409 {
4410 //puts (sf_strerror (NULL)); //removed 20100629 since now we search multiple places for file
4411 return 1;
4412 };
4413 if (sfinfo.channels < 1 || sfinfo.channels > 2)
4414 {
4415 SG_DBGOUT_INT ("Error : channels", sfinfo.channels);
4416 return 1;
4417 };
4418 //http://www.mega-nerd.com/libsndfile/FAQ.html
4419 //Q12 : I'm looking at sf_read*. What are items? What are frames?
4420 //For a sound file with only one channel, a frame is the same as a item.
4421 //For multi channel sound files, a single frame contains a single item for each channel.
4422 (*buffer) = (short *) malloc (sfinfo.frames * sizeof (short) * 2);
4423 (*size) = sfinfo.frames * 2;
4424 SG_DBGOUT_INT ("Channels:", sfinfo.channels);
4425 if (NULL == (*buffer))
4426 {
4427 return 1;
4428 }
4429 unsigned int readcount = 0;
4430
4431 if (2 == sfinfo.channels)
4432 {
4433 SG_DBGOUT ("Reading stereo data");
4434 readcount = sf_readf_short (sndfile, (*buffer), (sfinfo.frames));
4435 }
4436 else
4437 {
4438 SG_DBGOUT ("Reading mono data; converting to stereo");
4439 while (0 != sf_readf_short (sndfile, &((*buffer)[readcount]), 1))
4440 {
4441 ++readcount;
4442 (*buffer)[readcount] = (*buffer)[(readcount - 1)];
4443 ++readcount;
4444 }
4445 }
4446 sf_close (sndfile);
4447 SG_DBGOUT_INT ("readcount: ", readcount);
4448 SG_DBGOUT_INT ("framecount: ", (int) sfinfo.frames);
4449 SG_DBGOUT_INT ("array sizeof (bytes):",
4450 ((int) sfinfo.frames) * sizeof (short) * 2);
4451 SG_DBGOUT_INT ("array elements: (shorts)", (*size));
4452 return 0; //success
4453 // Gnaural.wav
4454 }
4455
4456 ///////////////////////////////////
4457 //pass this an empty buffer, and it fills it with sound from filename
4458 //and fills size with number of elements in the array of shorts.
4459 //returns 0 for success, filling (*buffer) and (*size) with data
4460 //return non-zero on failure, with (*buffer) = NULL (*size) = 0
main_LoadSoundFile(char * filename,int ** buffer,unsigned int * size)4461 int main_LoadSoundFile (char *filename, int **buffer, unsigned int *size)
4462 {
4463 SNDFILE *sndfile;
4464
4465 SF_INFO sfinfo;
4466
4467 if (0 != main_CheckSndfileVersion ())
4468 {
4469 return 1;
4470 }
4471
4472 (*buffer) = NULL;
4473 (*size) = 0;
4474 memset (&sfinfo, 0, sizeof (sfinfo));
4475 SG_DBGOUT_STR ("Loading PCM File:", filename);
4476 if (!(sndfile = sf_open (filename, SFM_READ, &sfinfo)))
4477 {
4478 //puts (sf_strerror (NULL)); //removed 20100629 since now we search multiple places for file
4479 return 1;
4480 };
4481 if (sfinfo.channels < 1 || sfinfo.channels > 2)
4482 {
4483 SG_DBGOUT_INT ("Error : channels", sfinfo.channels);
4484 return 1;
4485 };
4486 //http://www.mega-nerd.com/libsndfile/FAQ.html
4487 //Q12 : I'm looking at sf_read*. What are items? What are frames?
4488 //For a sound file with only one channel, a frame is the same as a item.
4489 //For multi channel sound files, a single frame contains a single item for each channel.
4490 (*buffer) = (int *) malloc (sfinfo.frames * sizeof (int));
4491 (*size) = sfinfo.frames;
4492 SG_DBGOUT_INT ("Channels:", sfinfo.channels);
4493 if (NULL == (*buffer))
4494 {
4495 return 1;
4496 }
4497 unsigned int readcount = 0;
4498
4499 if (2 == sfinfo.channels)
4500 {
4501 SG_DBGOUT ("Reading stereo data");
4502 readcount = sf_readf_short (sndfile, (short *) (*buffer), (sfinfo.frames));
4503 }
4504 else
4505 {
4506 SG_DBGOUT ("Reading mono data; converting to stereo");
4507 while (0 != sf_readf_short (sndfile, (short *) &((*buffer)[readcount]), 1))
4508 {
4509 (*buffer)[readcount] &= 0x0000FFFF;
4510 (*buffer)[readcount] |= (((*buffer)[readcount]) << 16);
4511 ++readcount;
4512 }
4513 }
4514 sf_close (sndfile);
4515 SG_DBGOUT_INT ("readcount: ", readcount);
4516 SG_DBGOUT_INT ("framecount: ", (int) sfinfo.frames);
4517 SG_DBGOUT_INT ("array sizeof (bytes):",
4518 ((int) sfinfo.frames) * sizeof (int));
4519 SG_DBGOUT_INT ("array elements: (shorts)", (*size));
4520 return 0; //success
4521 }
4522
4523 ///////////////////////////////////////////////
4524 //called whenever opening/re-initing a user file for data
main_UpdateGUI_UserDataInfo()4525 void main_UpdateGUI_UserDataInfo ()
4526 {
4527 //start checkboxes init
4528 gtk_toggle_button_set_active
4529 (main_checkbuttonSwapStereo, (BB_StereoSwap == 0) ? FALSE : TRUE);
4530
4531 gtk_toggle_button_set_active
4532 (main_checkbuttonOutputMono, (BB_Mono == 0) ? FALSE : TRUE);
4533 //end checkboxes init
4534 //start sliders init
4535 //now for the tricky overall vol and bal:
4536 if (BB_VolumeOverall_left > BB_VolumeOverall_right)
4537 {
4538 main_OverallVolume = BB_VolumeOverall_left;
4539 main_OverallBalance =
4540 -1.f + (BB_VolumeOverall_right / BB_VolumeOverall_left);
4541 }
4542 else if (BB_VolumeOverall_left < BB_VolumeOverall_right)
4543 {
4544 main_OverallVolume = BB_VolumeOverall_right;
4545 main_OverallBalance =
4546 1.f - (BB_VolumeOverall_left / BB_VolumeOverall_right);
4547 }
4548 else if (BB_VolumeOverall_left == BB_VolumeOverall_right)
4549 {
4550 main_OverallVolume = BB_VolumeOverall_right;
4551 main_OverallBalance = 0;
4552 }
4553 gtk_range_set_value ((GtkRange *) main_hscaleVolume,
4554 100 * main_OverallVolume);
4555 gtk_range_set_value ((GtkRange *) main_hscaleBalance,
4556 100 * main_OverallBalance);
4557 //end sliders init
4558 }
4559
4560 ///////////////////////////////////////////////
main_vscale_Y_button_event(GtkWidget * widget,gboolean pressed)4561 void main_vscale_Y_button_event (GtkWidget * widget, gboolean pressed)
4562 {
4563 main_vscale_Y_mousebuttondown = pressed;
4564 if (FALSE == main_vscale_Y_mousebuttondown)
4565 {
4566 gtk_range_set_value ((GtkRange *) widget, 0);
4567 }
4568 else
4569 {
4570 SG_BackupDataPoints (main_drawing_area); //a20070620
4571 }
4572 }
4573
4574 ///////////////////////////////////////////////
main_vscale_Y_value_change(GtkRange * range)4575 void main_vscale_Y_value_change (GtkRange * range)
4576 {
4577 if (TRUE == main_vscale_Y_mousebuttondown)
4578 {
4579 main_vscale_Y = -gtk_range_get_value (range);
4580 }
4581 else
4582 {
4583 main_vscale_Y = 0.0f;
4584 gtk_range_set_value ((GtkRange *) range, 0);
4585 }
4586 }
4587
4588 ///////////////////////////////////////////////
main_hscale_X_button_event(GtkWidget * widget,gboolean pressed)4589 void main_hscale_X_button_event (GtkWidget * widget, gboolean pressed)
4590 {
4591 main_hscale_X_mousebuttondown = pressed;
4592 if (FALSE == main_hscale_X_mousebuttondown)
4593 {
4594 gtk_range_set_value ((GtkRange *) widget, 0);
4595 }
4596 else
4597 {
4598 SG_BackupDataPoints (main_drawing_area); //a20070620
4599 }
4600 }
4601
4602 ///////////////////////////////////////////////
main_hscale_X_value_change(GtkRange * range)4603 void main_hscale_X_value_change (GtkRange * range)
4604 {
4605 if (TRUE == main_hscale_X_mousebuttondown)
4606 {
4607 main_hscale_X = gtk_range_get_value (range);
4608 }
4609 else
4610 {
4611 main_hscale_X = 0.0f;
4612 gtk_range_set_value ((GtkRange *) range, 0);
4613 }
4614 }
4615
4616 /////////////////////////////////////////////////////
4617 //a20070620:
main_slider_XY_handler(float vertical,float horizontal)4618 void main_slider_XY_handler (float vertical, float horizontal)
4619 {
4620 if (0.0f == vertical && 0.0f == horizontal)
4621 {
4622 return;
4623 }
4624
4625 if (vertical != 0.0f)
4626 {
4627 if (FALSE == main_XY_scaleflag)
4628 {
4629 SG_MoveSelectedDataPoints (main_drawing_area, 0, vertical);
4630 }
4631 else
4632 {
4633 SG_ScaleDataPoints_Y (main_drawing_area, 1 + (-.1 * vertical));
4634 }
4635 SG_ConvertYToData_SelectedPoints (main_drawing_area);
4636 }
4637
4638 //a20070620 :
4639 if (horizontal != 0.0f)
4640 {
4641 if (FALSE == main_XY_scaleflag)
4642 {
4643 SG_MoveSelectedDataPoints (main_drawing_area, horizontal, 0);
4644 }
4645 else
4646 {
4647 SG_ScaleDataPoints_Time (main_drawing_area, 1 + (.1 * horizontal));
4648 }
4649 SG_ConvertXToDuration_AllPoints (main_drawing_area);
4650 }
4651
4652 SG_ConvertDataToXY (main_drawing_area); // NOTE: I need to call this both to set limits and to bring XY's outside of graph back in
4653 SG_DrawGraph (main_drawing_area);
4654 SG_DataHasChanged = SG_GraphHasChanged = TRUE; //20070105 tricky way to do main_LoadBinauralBeatSoundEngine in a bulk way
4655 }
4656
4657 ///////////////////////////////////
main_RoundValues()4658 void main_RoundValues ()
4659 {
4660 static double val = 100;
4661
4662 static double param = 0;
4663
4664 char namestr[] = "Round Selected DPs";
4665
4666 if (TRUE ==
4667 main_AskUserForNumberDialog
4668 (namestr,
4669 "Round which parameter?\n0: duration\n1: volume_left\n2: volume_right\n3: basefreq\n4: beatfreq",
4670 ¶m)
4671 && TRUE == main_AskUserForNumberDialog (namestr,
4672 "Rounding value:\n1=1th\n10=10th\n100=100th",
4673 &val))
4674 {
4675 SG_BackupDataPoints (main_drawing_area);
4676 SG_RoundValues_All (main_drawing_area, val, param);
4677 }
4678 }
4679
4680 ///////////////////////////////////
4681 //this launches or ends the thread that runs GnauralNet:
main_GnauralNet_StopStart(GtkMenuItem * menuitem)4682 void main_GnauralNet_StopStart (GtkMenuItem * menuitem)
4683 {
4684 GtkWidget *menu_label = gtk_bin_get_child (GTK_BIN (menuitem));
4685
4686 if (GNAURALNET_STOPPED == GN_My.State)
4687 {
4688 if (GNAURALNET_SUCCESS == GN_start (NULL, NULL)) //NULL,NULL uses both default internal funcs in GN for loop and processing
4689 {
4690 gtk_label_set_text (GTK_LABEL (menu_label), "Stop Gnauralnet Server");
4691 return;
4692 }
4693 }
4694 GN_stop ();
4695 gtk_label_set_text (GTK_LABEL (menu_label), "Start Gnauralnet Server");
4696 }
4697
4698 ///////////////////////////////////
main_GnauralNet_JoinFriend(GtkMenuItem * menuitem)4699 void main_GnauralNet_JoinFriend (GtkMenuItem * menuitem)
4700 {
4701 if (GNAURALNET_RUNNING != GN_My.State)
4702 {
4703 main_MessageDialogBox
4704 ("First start GnauralNet", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
4705 return;
4706 }
4707 main_AskUserForIP_Port
4708 ("Connect to another Gnaural", "Enter Port and IP", "127.0.0.1", 7141,
4709 GN_MSGTYPE_TIMEINFO);
4710 }
4711
4712 ///////////////////////////////////
main_GnauralNet_PhaseInfo(GtkMenuItem * menuitem)4713 void main_GnauralNet_PhaseInfo (GtkMenuItem * menuitem)
4714 {
4715 if (GNAURALNET_RUNNING != GN_My.State)
4716 {
4717 main_MessageDialogBox
4718 ("First start GnauralNet", GTK_MESSAGE_INFO, GTK_BUTTONS_OK);
4719 return;
4720 }
4721
4722 // if (BB_UserFunc == NULL)
4723 main_AskUserForIP_Port
4724 ("Send Phase Info To Remote App", "Enter Port and IP", "127.0.0.1", 7141,
4725 GN_MSGTYPE_PHASEINFO);
4726 }
4727
4728 ///////////////////////////////////
main_GnauralNet_ShowInfo(GtkMenuItem * menuitem)4729 void main_GnauralNet_ShowInfo (GtkMenuItem * menuitem)
4730 {
4731 GN_ShowInfo ();
4732 }
4733
4734 ///////////////////////////////////////////
4735 //returns TRUE if user clicked OK, filling val with result
main_AskUserForIP_Port(char * title,char * question,char * IP,unsigned short Port,int type)4736 void main_AskUserForIP_Port (char *title, char *question, char *IP,
4737 unsigned short Port, int type)
4738 {
4739 GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
4740 (GtkWindow *) main_window,
4741 (GtkDialogFlags)
4742 (GTK_DIALOG_MODAL |
4743 GTK_DIALOG_DESTROY_WITH_PARENT),
4744 GTK_STOCK_OK,
4745 GTK_RESPONSE_ACCEPT,
4746 GTK_STOCK_CANCEL,
4747 GTK_RESPONSE_REJECT, NULL);
4748
4749 //add some stuff:
4750 GtkWidget *label = gtk_label_new (question);
4751
4752 GtkWidget *entry_InputIP = gtk_entry_new ();
4753
4754 GtkWidget *entry_InputPort = gtk_entry_new ();
4755
4756 sprintf (main_sTmp, "%s", IP);
4757 gtk_entry_set_text (GTK_ENTRY (entry_InputIP), main_sTmp);
4758 sprintf (main_sTmp, "%d", Port);
4759 gtk_entry_set_text (GTK_ENTRY (entry_InputPort), main_sTmp);
4760 // Add the label, and show everything we've added to the dialog.
4761 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), label);
4762 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), entry_InputIP);
4763 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
4764 entry_InputPort);
4765 gtk_widget_show_all (dialog);
4766 //block until I get a response:
4767 gint result = gtk_dialog_run (GTK_DIALOG (dialog));
4768
4769 switch (result)
4770 {
4771 case GTK_RESPONSE_ACCEPT:
4772 {
4773 unsigned short port =
4774 (unsigned short) atoi (gtk_entry_get_text (GTK_ENTRY (entry_InputPort)));
4775 unsigned int ip =
4776 inet_addr (gtk_entry_get_text (GTK_ENTRY (entry_InputIP)));
4777 switch (type)
4778 {
4779 case GN_MSGTYPE_TIMEINFO:
4780 GN_FriendList_Invite (ip, htons (port));
4781 break;
4782
4783 case GN_MSGTYPE_PHASEINFO:
4784 main_PhaseInfoToggle (ip, htons (port));
4785 break;
4786
4787 default:
4788 //SG_DBGOUT ("Shouldn't be here");
4789 break;
4790 }
4791
4792 }
4793 break;
4794 default:
4795 break;
4796 }
4797 gtk_widget_destroy (dialog);
4798 }
4799
4800 ////////////////////////////////////
4801 //checks if there actually is a sndfile library available
4802 //returns 0 on success
main_CheckSndfileVersion(void)4803 int main_CheckSndfileVersion (void)
4804 {
4805 main_sTmp[0] = 0;
4806 sf_command (NULL, SFC_GET_LIB_VERSION, main_sTmp, sizeof (main_sTmp));
4807 if (strlen (main_sTmp) < 1)
4808 {
4809 SG_ERROUT ("Couldn't get sndfile version; did you install it?");
4810 main_MessageDialogBox ("You need to install the sndfile library",
4811 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK);
4812 return -1;
4813 };
4814 SG_DBGOUT_STR ("Your sndfile version:", main_sTmp);
4815 return 0;
4816 }
4817
4818 ////////////////////////////////////
4819 //a20080307
main_progressbar_button_press_event(GtkWidget * widget,GdkEventButton * event)4820 void main_progressbar_button_press_event (GtkWidget * widget,
4821 GdkEventButton * event)
4822 {
4823 while (TRUE == BB_InCriticalLoopFlag)
4824 {
4825 SG_DBGOUT ("In critical loop, waiting");
4826 main_Sleep (100);
4827 }
4828
4829 float x = event->x / (float) widget->allocation.width;
4830
4831 double samplecount_oneloop = BB_TotalDuration * BB_AUDIOSAMPLERATE;
4832
4833 if (1 < BB_Loops)
4834 {
4835 SG_DBGOUT_INT ("factoring progressbar loops:", BB_Loops);
4836 double samplecount_total = samplecount_oneloop * BB_Loops;
4837
4838 //first get the total number of samples user clicked at:
4839 BB_CurrentSampleCountLooped = (unsigned long) (x * samplecount_total);
4840 //now translate that to BB_LoopCount:
4841 BB_LoopCount =
4842 BB_Loops -
4843 (BB_CurrentSampleCountLooped / (unsigned long) samplecount_oneloop);
4844 //now figured out how far in a single schedule pass we were:
4845 BB_CurrentSampleCount =
4846 (BB_CurrentSampleCountLooped % (unsigned long) samplecount_oneloop);
4847 //now subtract that many samples from BB_CurrentSampleCountLooped:
4848 BB_CurrentSampleCountLooped -= BB_CurrentSampleCount;
4849 GN_DBGOUT_UINT ("New BB_LoopCount:", BB_LoopCount);
4850 main_UpdateGUI_entryLoops ();
4851 main_UpdateGUI_ProjectedRuntime ();
4852 }
4853 else
4854 {
4855 SG_DBGOUT ("factoring simple progressbar");
4856 //easy way:
4857 BB_CurrentSampleCount = (unsigned long) (samplecount_oneloop * x);
4858 }
4859
4860 //this ensures that no sync'd friends follow this one now:
4861 GN_Time_ResetSeniority ();
4862 }
4863
4864 /////////////////////////////////
main_playAudio_SoundInit()4865 int main_playAudio_SoundInit ()
4866 {
4867 int res = playAudio_SoundInit (main_sTmp);
4868
4869 if (main_gnaural_guiflag == TRUE)
4870 {
4871 main_UpdateGUI_Statusbar (main_sTmp, "");
4872 }
4873 else
4874 {
4875 BB_DBGOUT (main_sTmp);
4876 }
4877 return res;
4878 }
4879
4880 ///////////////////////////////////
main_Preferences()4881 void main_Preferences ()
4882 {
4883 main_SelectFont ();
4884 }
4885
4886 /////////////////////////////////
4887 //20100408
4888 //will eventually want to set the current font name in dialog
main_SelectFont()4889 void main_SelectFont ()
4890 {
4891 GtkResponseType result;
4892 GtkWidget *dialog = gtk_font_selection_dialog_new ("Select Font");
4893 result = gtk_dialog_run (GTK_DIALOG (dialog));
4894 if (result == GTK_RESPONSE_OK || result == GTK_RESPONSE_APPLY)
4895 {
4896 PangoFontDescription *font_desc;
4897 gchar *fontname =
4898 gtk_font_selection_dialog_get_font_name (GTK_FONT_SELECTION_DIALOG
4899 (dialog));
4900 font_desc = pango_font_description_from_string (fontname);
4901 double dpi = gdk_screen_get_resolution (gdk_screen_get_default ());
4902 if (TRUE == pango_font_description_get_size_is_absolute (font_desc))
4903 {
4904 SG_GraphFontSize =
4905 pango_font_description_get_size (font_desc) / PANGO_SCALE;
4906 }
4907 else
4908 {
4909 SG_GraphFontSize =
4910 (dpi * pango_font_description_get_size (font_desc) / PANGO_SCALE) / 72.0;
4911 }
4912 SG_DBGOUT_INT ("New font size:", SG_GraphFontSize);
4913 pango_layout_set_font_description (SG_PangoLayout, font_desc);
4914 pango_font_description_free (font_desc);
4915 g_free (fontname);
4916 }
4917 gtk_widget_destroy (dialog);
4918 }
4919
4920 ///////////////////////////////////////////
main_DataPointSize()4921 void main_DataPointSize ()
4922 {
4923 double val = (double) SG_DataPointSize;
4924 main_AskUserForNumberDialog (main_sTmp, "Data Point Size", &val);
4925 SG_DataPointSize = (int) val;
4926 }
4927
4928 ///////////////////////////////////////////
4929 //added 20101006 to handle SG's magnetic pointer
main_on_checkbutton_MagneticPointer_toggled(GtkToggleButton * togglebutton)4930 void main_on_checkbutton_MagneticPointer_toggled (GtkToggleButton *
4931 togglebutton)
4932 {
4933 SG_MagneticPointerflag = gtk_toggle_button_get_active (togglebutton);
4934 }
4935
4936 #ifndef GNAURAL_MACOSX
4937 #ifndef GNAURAL_WIN32
4938 //////////////////////////////////////////////
4939 //20101109: Returns elapsed time since previous call. First call it
4940 //returns 0.
4941 //NOTE: used to use gettimeofday (&newtime, NULL), but this is better.
Stopwatch()4942 double Stopwatch ()
4943 {
4944 #define BILLION 1000000000L;
4945 struct timespec newtime;
4946 static struct timespec oldtime;
4947 double result;
4948
4949 if (clock_gettime (CLOCK_REALTIME, &newtime) == -1)
4950 {
4951 SG_DBGOUT ("clock gettime");
4952 return 0;
4953 }
4954
4955 result = (newtime.tv_sec - oldtime.tv_sec)
4956 + (double) (newtime.tv_nsec - oldtime.tv_nsec) / (double) BILLION;
4957
4958 //deal with first-time call
4959 if (0 == oldtime.tv_nsec && 0 == oldtime.tv_sec)
4960 {
4961 result = 0;
4962 }
4963
4964 oldtime.tv_sec = newtime.tv_sec;
4965 oldtime.tv_nsec = newtime.tv_nsec;
4966
4967 return result;
4968 }
4969 #endif
4970 #endif
4971
4972 unsigned int IP = 0;
4973 unsigned short Port = 0;
4974 //////////////////////////////////////////////
4975 //20110221: BB will call this is function if non-NULL
main_BB_UserFunc(int voice)4976 void main_BB_UserFunc (int voice)
4977 {
4978 if (GNAURALNET_RUNNING == GN_My.State)
4979 {
4980 char data = voice;
4981 GN_Socket_SendMessage (GN_My.Socket, (char *) &data,
4982 sizeof (data), IP, Port);
4983 }
4984 }
4985
4986 //////////////////////////////////////////////
4987 //20110221: toggles state of function from valid to NULL
4988 //so BB can pulse out phase info
main_PhaseInfoToggle(unsigned int ip,unsigned short port)4989 void main_PhaseInfoToggle (unsigned int ip, unsigned short port)
4990 {
4991 IP = ip;
4992 Port = port;
4993 if (BB_UserFunc != NULL)
4994 {
4995 BB_UserFunc = NULL;
4996 }
4997 else
4998 {
4999 BB_UserFunc = main_BB_UserFunc;
5000 }
5001 }
5002
5003 //////////////////////////////////////////////
5004 //20110222:hack tailored to an SG function used to getting mouse clicks
main_DPPropertiesDialog()5005 void main_DPPropertiesDialog ()
5006 {
5007 GdkEventButton event;
5008 event.type = GDK_KEY_PRESS;
5009 event.window = NULL;
5010 event.send_event = 0;
5011 event.time = 0;
5012 event.x = -100.0;
5013 event.y = -100.0;
5014 event.axes = NULL;
5015 event.state = 0;
5016 event.button = 3;
5017 event.device = NULL;
5018 event.x_root = 0;
5019 event.y_root = 0;
5020 SG_button_press_event (main_drawing_area, &event);
5021 }
5022