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       &param)
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