1 #include "snd.h"
2 #include "snd-file.h"
3 #include "sndlib-strings.h"
4 
5 
6 /* -------------------------------- basic file attributes -------------------------------- */
7 
8 #ifdef _MSC_VER
disk_kspace(const char * filename)9   mus_long_t disk_kspace(const char *filename) {return(1234567);}
10 #else
11 
12   #include <sys/statvfs.h>
13   #include <sys/param.h>
14   #include <dirent.h>
15 
16 #if __bsdi__ || __NetBSD__
17   #include <sys/mount.h>
18 #endif
19 
disk_kspace(const char * filename)20 mus_long_t disk_kspace(const char *filename)
21 {
22 #if HAVE_SUN
23   statvfs_t buf; /* else dumb compiler complaint */
24 #else
25   struct statvfs buf;
26 #endif
27   mus_long_t err;
28   err = statvfs(filename, &buf);
29   if (err == 0)
30     {
31       if (buf.f_frsize == 1024)
32 	return(buf.f_bfree);
33       return((mus_long_t)(buf.f_frsize * ((double)(buf.f_bfree) / 1024.0)));
34     }
35   return(err);
36 }
37 #endif
38 
39 
is_link_file(const char * filename)40 bool is_link_file(const char *filename)
41 {
42 #if __MINGW32__
43   return(false);
44 #else
45   struct stat statbuf;
46 #ifndef _MSC_VER
47   return((stat(filename, &statbuf) >= 0) &&
48 	 (S_ISLNK(statbuf.st_mode)));
49 #else
50   return((stat(filename, &statbuf) == 0) &&
51 	 (S_ISLNK(statbuf.st_mode)));
52 #endif
53 #endif
54 }
55 
56 
is_directory(const char * filename)57 bool is_directory(const char *filename)
58 {
59   struct stat statbuf;
60 #ifndef _MSC_VER
61   return((stat(filename, &statbuf) >= 0) &&
62 	 (S_ISDIR(statbuf.st_mode)));
63 #else
64   return((stat(filename, &statbuf) == 0) &&
65 	 (S_ISDIR(statbuf.st_mode)));
66 #endif
67 }
68 
69 
file_write_date(const char * filename)70 time_t file_write_date(const char *filename)
71 {
72   struct stat statbuf;
73   int err;
74   if (!filename) return((time_t)0);
75   err = stat(filename, &statbuf);
76   if (err < 0) return((time_t)err);
77   return((time_t)(statbuf.st_mtime));
78 }
79 
80 
short_sample_type_name(mus_sample_t sndlib_sample_type,const char * filename)81 const char *short_sample_type_name(mus_sample_t sndlib_sample_type, const char *filename)
82 {
83   if (mus_is_sample_type(sndlib_sample_type))
84     return(mus_sample_type_short_name(sndlib_sample_type));
85   else return(mus_header_original_sample_type_name(mus_sound_original_sample_type(filename),
86 						   mus_sound_header_type(filename)));
87 }
88 
89 
forget_filename(const char * filename,char ** names)90 static void forget_filename(const char *filename, char **names)
91 {
92   int i, j = 0;
93   for (i = 0; i < FILENAME_LIST_SIZE; i++)
94     if ((names[i]) &&
95 	(strcmp(names[i], filename) == 0))
96       {
97 	free(names[i]);
98 	names[i] = NULL;
99       }
100   for (i = 0; i < FILENAME_LIST_SIZE; i++)
101     if (names[i])
102       {
103 	if (i != j)
104 	  {
105 	    names[j] = names[i];
106 	    names[i] = NULL;
107 	  }
108 	j++;
109       }
110 }
111 
112 
remember_filename(const char * filename,char ** names)113 void remember_filename(const char *filename, char **names)
114 {
115   int i;
116   forget_filename(filename, names); /* clear out old copy, if any, then compact the list */
117   if (names[FILENAME_LIST_SIZE - 1]) free(names[FILENAME_LIST_SIZE - 1]);
118   for (i = FILENAME_LIST_SIZE - 1; i > 0; i--)
119     names[i] = names[i - 1];
120   names[0] = mus_strdup(filename);
121 }
122 
123 
make_filename_list(void)124 char **make_filename_list(void)
125 {
126   return((char **)calloc(FILENAME_LIST_SIZE, sizeof(char *)));
127 }
128 
129 
130 static char *preloaded_files[FILENAME_LIST_SIZE];
131 
preload_filenames(char ** files)132 void preload_filenames(char **files)
133 {
134   int i;
135   for (i = 0; (i < FILENAME_LIST_SIZE) && (preloaded_files[i]); i++)
136     files[i] = mus_strdup(preloaded_files[i]);
137 }
138 
139 
recent_files_size(void)140 int recent_files_size(void)
141 {
142   /* return how many files in the preloaded list are not currently open (for Open Recent menu item) */
143   int i, size = 0;
144   for (i = 0; (i < FILENAME_LIST_SIZE) && (preloaded_files[i]); i++)
145     if ((!(find_sound(preloaded_files[i], 0))) &&
146 	(mus_file_probe(preloaded_files[i])))
147       size++;
148   return(size);
149 }
150 
151 
recent_files(void)152 char **recent_files(void)
153 {
154   int i, j = 0;
155   char **new_list;
156   new_list = make_filename_list();
157   for (i = 0; (i < FILENAME_LIST_SIZE) && (preloaded_files[i]); i++)
158     if ((!(find_sound(preloaded_files[i], 0))) &&
159 	(mus_file_probe(preloaded_files[i])))
160       new_list[j++] = mus_strdup(preloaded_files[i]);
161   return(new_list);
162 }
163 
164 
165 /* -------------------------------- file filters -------------------------------- */
166 
167 #define INITIAL_FILE_FILTERS_SIZE 4
168 
g_expand_vector(Xen vector,int new_size)169 Xen g_expand_vector(Xen vector, int new_size)
170 {
171   int i, len;
172   Xen new_vect;
173   len = Xen_vector_length(vector);
174   new_vect = Xen_make_vector(new_size, Xen_false);
175   Xen_GC_protect(new_vect);
176   for (i = 0; i < len; i++)
177     {
178       Xen_vector_set(new_vect, i, Xen_vector_ref(vector, i));
179       Xen_vector_set(vector, i, Xen_false);
180     }
181 #if HAVE_RUBY || HAVE_FORTH
182   Xen_GC_unprotect(vector);
183 #endif
184   return(new_vect);
185 }
186 
187 
file_filter_ok(Xen name,Xen proc,const char * caller)188 static bool file_filter_ok(Xen name, Xen proc, const char *caller)
189 {
190   char *errmsg;
191   Xen_check_type(Xen_is_string(name), name, 1, caller, "a string");
192   Xen_check_type(Xen_is_procedure(proc), proc, 2, caller, "a procedure of 1 arg (filename)");
193   errmsg = procedure_ok(proc, 1, caller, "file filter", 2);
194   if (errmsg)
195     {
196       Xen errstr;
197       errstr = C_string_to_Xen_string(errmsg);
198       free(errmsg);
199       snd_bad_arity_error(caller, errstr, proc);
200       return(false);
201     }
202   return(true);
203 }
204 
205 
g_add_file_filter(Xen name,Xen proc)206 static Xen g_add_file_filter(Xen name, Xen proc)
207 {
208   #define H_add_file_filter "(" S_add_file_filter " name proc) -- add proc with identifier name to file filter list. \n\
209   (add-file-filter \"just .snd\" \n\
210     (lambda (name) \n\
211       (string=? \".snd\" (substring name (- (length name) 4)))))\n\
212   restricts the displayed files to .snd files."
213 
214   if (file_filter_ok(name, proc, S_add_file_filter))
215     {
216       int i, len;
217       len = ss->file_filters_size;
218       for (i = 0; i < len; i++)
219 	{
220 	  if (Xen_is_false(Xen_vector_ref(ss->file_filters, i)))
221 	    {
222 	      Xen_vector_set(ss->file_filters, i, Xen_list_2(name, proc));
223 	      return(C_int_to_Xen_integer(i));
224 	    }
225 	}
226       ss->file_filters_size = len * 2;
227       ss->file_filters = g_expand_vector(ss->file_filters, ss->file_filters_size);
228       Xen_vector_set(ss->file_filters, len, Xen_list_2(name, proc));
229       return(C_int_to_Xen_integer(len));
230     }
231   return(Xen_false);
232 }
233 
234 
g_delete_file_filter(Xen index)235 static Xen g_delete_file_filter(Xen index)
236 {
237   #define H_delete_file_filter "(" S_delete_file_filter " index) -- delete proc with identifier index from file filter list"
238   int pos;
239   Xen_check_type(Xen_is_integer(index), index, 1, S_delete_file_filter, "a file-filter function index");
240   pos = Xen_integer_to_C_int(index);
241   if ((pos >= 0) &&
242       (pos < ss->file_filters_size))
243     {
244       Xen_vector_set(ss->file_filters, pos, Xen_false);
245     }
246   return(index);
247 }
248 
249 
250 
251 
252 
253 /* -------------------------------- directory readers -------------------------------- */
254 
make_dir_info(const char * name)255 static dir_info *make_dir_info(const char *name)
256 {
257   dir_info *dp;
258   dp = (dir_info *)calloc(1, sizeof(dir_info));
259   dp->files = (sort_info **)calloc(32, sizeof(sort_info *));
260   dp->dir_name = mus_strdup(name);
261   dp->len = 0;
262   dp->size = 32;
263   return(dp);
264 }
265 
266 
make_sort_info(const char * filename,const char * full_filename)267 static sort_info *make_sort_info(const char *filename, const char *full_filename)
268 {
269   sort_info *ptr;
270   ptr = (sort_info *)calloc(1, sizeof(sort_info));
271   ptr->filename = mus_strdup(filename); /* not mus_strdup -> these are glomming up memlog */
272   ptr->full_filename = mus_strdup(full_filename);
273   return(ptr);
274 }
275 
276 
free_sort_info(sort_info * ptr)277 static sort_info *free_sort_info(sort_info *ptr)
278 {
279   if (ptr)
280     {
281       if (ptr->filename) free(ptr->filename);
282       if (ptr->full_filename) free(ptr->full_filename);
283       free(ptr);
284     }
285   return(NULL);
286 }
287 
free_dir_info(dir_info * dp)288 dir_info *free_dir_info(dir_info *dp)
289 {
290   if (dp->dir_name) free(dp->dir_name);
291   if (dp->files)
292     {
293       int i;
294       for (i = 0; i < dp->len; i++)
295 	if (dp->files[i])
296 	  dp->files[i] = free_sort_info(dp->files[i]);
297       free(dp->files);
298     }
299   free(dp);
300   return(NULL);
301 }
302 
303 
add_filename_to_dir_info(dir_info * dp,const char * name,const char * fullname)304 static void add_filename_to_dir_info(dir_info *dp, const char *name, const char *fullname)
305 {
306   dp->files[dp->len] = make_sort_info(name, fullname);
307   dp->len++;
308   if (dp->len == dp->size)
309     {
310       int i;
311       dp->size += 32;
312       dp->files = (sort_info **)realloc(dp->files, dp->size * sizeof(sort_info *));
313       for (i = dp->size - 32; i < dp->size; i++) dp->files[i] = NULL;
314     }
315 }
316 
317 
load_dir(DIR * dpos,dir_info * dp,bool (* filter)(const char * filename))318 static void load_dir(DIR *dpos, dir_info *dp, bool (*filter)(const char *filename))
319 {
320   struct dirent *dirp;
321   char *fullname;
322   int fullname_start = 0, path_max = 0;
323 
324 #ifndef _MSC_VER
325   path_max = pathconf("/", _PC_PATH_MAX);
326 #endif
327   if (path_max < 1024)
328     {
329 #if defined(PATH_MAX)
330       path_max = PATH_MAX;
331       if (path_max < 1024)
332 #endif
333 	path_max = 1024;
334     }
335 
336   fullname = (char *)calloc(path_max, sizeof(char));
337   strcopy(fullname, dp->dir_name, path_max);
338   fullname_start = strlen(dp->dir_name);
339   while ((dirp = readdir(dpos)))
340     if (dirp->d_name[0] != '.')
341       {
342 	strcat(fullname, dirp->d_name);
343 	if (filter(fullname))
344 	  add_filename_to_dir_info(dp, dirp->d_name, fullname);
345 	fullname[fullname_start] = '\0';
346       }
347   free(fullname);
348 }
349 
350 
not_is_directory(const char * name)351 static bool not_is_directory(const char *name)
352 {
353   return(!(is_directory(name)));
354 }
355 
356 
find_files_in_dir(const char * name)357 dir_info *find_files_in_dir(const char *name)
358 {
359 #ifdef _MSC_VER
360   return(NULL);
361 #else
362   DIR *dpos;
363   dir_info *dp = NULL;
364   dpos = opendir(name);
365   if (dpos)
366     {
367       dp = make_dir_info(name);
368       load_dir(dpos, dp, not_is_directory);
369       if (closedir(dpos) != 0)
370 	snd_error("closedir %s failed (%s)!",
371 		  name, snd_io_strerror());
372     }
373   return(dp);
374 #endif
375 }
376 
377 
378 static Xen filter_func;
379 
filter_xen(const char * name)380 static bool filter_xen(const char *name)
381 {
382   return(Xen_boolean_to_C_bool(Xen_call_with_1_arg(filter_func, C_string_to_Xen_string(name), "filter func")));
383 }
384 
385 
find_filtered_files_in_dir(const char * name,int filter_choice)386 dir_info *find_filtered_files_in_dir(const char *name, int filter_choice)
387 {
388 #ifdef _MSC_VER
389   return(NULL);
390 #else
391   DIR *dpos;
392   dir_info *dp = NULL;
393   if ((dpos = opendir(name)))
394     {
395       bool (*filter)(const char *filename);
396       if (filter_choice == JUST_SOUNDS_FILTER)
397 	filter = is_sound_file;
398       else
399 	{
400 	  int filter_pos;
401 	  filter = filter_xen;
402 	  filter_pos = filter_choice - 2;
403 	  if ((filter_pos >= 0) &&
404 	      (filter_pos < ss->file_filters_size))
405 	    filter_func = Xen_cadr(Xen_vector_ref(ss->file_filters, filter_pos));
406 	  else
407 	    {
408 	      snd_warning("no such file-filter (%d)", filter_pos);
409 	      closedir(dpos);
410 	      return(find_files_in_dir(name));
411 	    }
412 	}
413 
414       dp = make_dir_info(name);
415       load_dir(dpos, dp, filter);
416 
417       if (closedir(dpos) != 0)
418 	snd_error("closedir %s failed (%s)!",
419 		  name, snd_io_strerror());
420     }
421   return(dp);
422 #endif
423 }
424 
425 
426 static dir_info *find_files_from_pattern(dir_info *dp, const char *pattern);
427 
find_filtered_files_in_dir_with_pattern(const char * name,int filter_choice,const char * pattern)428 dir_info *find_filtered_files_in_dir_with_pattern(const char *name, int filter_choice, const char *pattern)
429 {
430   dir_info *full_dir, *pattern_dir;
431   if (filter_choice != NO_FILE_FILTER)
432     full_dir = find_filtered_files_in_dir(name, filter_choice);
433   else full_dir = find_files_in_dir(name);
434   pattern_dir = find_files_from_pattern(full_dir, pattern);
435   free_dir_info(full_dir);
436   return(pattern_dir);
437 }
438 
439 
440 /* -------- sound file extensions list -------- */
441 
442 static char **sound_file_extensions = NULL;
443 static int sound_file_extensions_size = 0;
444 static int sound_file_extensions_end = 0;
445 static int default_sound_file_extensions = 0;
446 
get_sound_file_extensions(void)447 const char **get_sound_file_extensions(void) {return((const char **)sound_file_extensions);}
sound_file_extensions_length(void)448 int sound_file_extensions_length(void) {return(sound_file_extensions_end);}
449 
add_sound_file_extension(const char * ext)450 static void add_sound_file_extension(const char *ext)
451 {
452   int i;
453   if ((!ext) || (!(*ext))) return;
454 
455   for (i = 0; i < sound_file_extensions_end; i++)
456     if (strcmp(ext, sound_file_extensions[i]) == 0)
457       return;
458   if (sound_file_extensions_end == sound_file_extensions_size)
459     {
460       sound_file_extensions_size += 8;
461       if (!sound_file_extensions)
462 	sound_file_extensions = (char **)calloc(sound_file_extensions_size, sizeof(char *));
463       else sound_file_extensions = (char **)realloc(sound_file_extensions, sound_file_extensions_size * sizeof(char *));
464     }
465   sound_file_extensions[sound_file_extensions_end] = mus_strdup(ext);
466   sound_file_extensions_end++;
467 }
468 
469 
init_sound_file_extensions(void)470 void init_sound_file_extensions(void)
471 {
472   add_sound_file_extension("snd");
473   add_sound_file_extension("aiff");
474   add_sound_file_extension("aif");
475   add_sound_file_extension("wav");
476   add_sound_file_extension("WAV");
477   add_sound_file_extension("au");
478   add_sound_file_extension("aifc");
479   add_sound_file_extension("voc");
480   add_sound_file_extension("wve");
481   add_sound_file_extension("sf2");
482   add_sound_file_extension("rf64");
483   add_sound_file_extension("caf");
484 
485 #if HAVE_OGG
486   add_sound_file_extension("ogg");
487 #endif
488 
489 #if HAVE_SPEEX
490   add_sound_file_extension("speex"); /* ?? */
491 #endif
492 
493 #if HAVE_FLAC
494   add_sound_file_extension("flac");
495 #endif
496 
497 #if HAVE_MIDI
498   add_sound_file_extension("mid");
499 #endif
500 
501 #if HAVE_MPEG
502   add_sound_file_extension("mpeg");
503   add_sound_file_extension("mp3");
504 #endif
505 
506 #if HAVE_WAVPACK
507   add_sound_file_extension("wv");
508 #endif
509 
510   default_sound_file_extensions = sound_file_extensions_end;
511 }
512 
513 
save_added_sound_file_extensions(FILE * fd)514 void save_added_sound_file_extensions(FILE *fd)
515 {
516   int i;
517   if (sound_file_extensions_end > default_sound_file_extensions)
518     for (i = default_sound_file_extensions; i < sound_file_extensions_end; i++)
519       {
520 #if HAVE_SCHEME
521 	fprintf(fd, "(%s \"%s\")\n", S_add_sound_file_extension, sound_file_extensions[i]);
522 #endif
523 
524 #if HAVE_RUBY
525 	fprintf(fd, "%s(\"%s\")\n", to_proc_name(S_add_sound_file_extension), sound_file_extensions[i]);
526 #endif
527 
528 #if HAVE_FORTH
529 	fprintf(fd, "\"%s\" %s drop\n", sound_file_extensions[i], S_add_sound_file_extension);
530 #endif
531       }
532 }
533 
534 
535 /* mus_header_read here (or stripped-down equivalent) was very slow, and is just as easy to
536  * fool as an extension check (file might start with the word ".snd" or whatever).
537  */
538 
is_sound_file(const char * name)539 bool is_sound_file(const char *name)
540 {
541   int i, dot_loc = -1, len;
542   if (!name) return(false);
543   len = mus_strlen(name);
544   for (i = 0; i < len; i++)
545     if (name[i] == '.')
546       dot_loc = i;
547   /* dot_loc is last dot in the name */
548   if ((dot_loc > 0) &&
549       (dot_loc < len - 1))
550     {
551       const char *ext;
552       ext = (const char *)(name + dot_loc + 1);
553       for (i = 0; i < sound_file_extensions_end; i++)
554 	if (mus_strcmp(ext, sound_file_extensions[i]))
555 	  return(true);
556     }
557   return(false);
558 }
559 
560 
names_match(const char * filename,const char * pattern)561 static bool names_match(const char *filename, const char *pattern)
562 {
563   /* just "*" for wildcards here */
564   const char *sn, *sp;
565   sn = filename;
566   sp = pattern;
567   if ((!sn) || (!sp))
568     {
569       if ((sn) || (sp))
570 	return(false);
571       else return(true);
572     }
573   while ((*sn) && (*sp))
574     {
575       if ((*sp) == '*')
576 	{
577 	  sp++;
578 	  while ((*sp) == '*') sp++;
579 	  if (!(*sp)) return(true);
580 	  while ((*sn) && ((*sn) != (*sp))) sn++;
581 	  if (!(*sn)) return(false);
582 	}
583       else
584 	{
585 	  if ((*sn) != (*sp)) return(false);
586 	  sn++;
587 	  sp++;
588 	}
589     }
590   return(true);
591 }
592 
593 
find_files_from_pattern(dir_info * dp,const char * pattern)594 static dir_info *find_files_from_pattern(dir_info *dp, const char *pattern)
595 {
596   int i;
597   dir_info *ndp;
598   ndp = make_dir_info(dp->dir_name);
599   for (i = 0; i < dp->len; i++)
600     if (names_match(dp->files[i]->filename, pattern))
601       add_filename_to_dir_info(ndp, dp->files[i]->filename, dp->files[i]->full_filename);
602   return(ndp);
603 }
604 
605 
606 /* -------------------------------- Snd title (lists open sounds) -------------------------------- */
607 
608 typedef struct {
609   int active_sounds;
610   char **names;
611   int *sounds;
612 } active_sound_list;
613 
add_sound_to_active_list(snd_info * sp,void * sptr1)614 static void add_sound_to_active_list(snd_info *sp, void *sptr1)
615 {
616   active_sound_list *sptr = (active_sound_list *)sptr1;
617   sptr->names[sptr->active_sounds] = sp->filename;
618   sptr->sounds[sptr->active_sounds] = sp->index;
619   (sptr->active_sounds)++;
620 }
621 
622 
reflect_file_change_in_title(void)623 void reflect_file_change_in_title(void)
624 {
625   char *title_buffer = NULL;
626   active_sound_list *alist;
627   int i, j, len;
628 
629   alist = (active_sound_list *)calloc(1, sizeof(active_sound_list));
630   alist->sounds = (int *)calloc(ss->max_sounds, sizeof(int));
631   alist->names = (char **)calloc(ss->max_sounds, sizeof(char *));
632   for_each_sound_with_void(add_sound_to_active_list, (void *)alist);
633 
634   len = mus_strlen(ss->startup_title) + 32;
635   if (alist->active_sounds > 0)
636     {
637       if (alist->active_sounds < 4)
638 	j = alist->active_sounds;
639       else j = 4;
640       for (i = 0; i < j; i++)
641 	len += mus_strlen(filename_without_directory(alist->names[i]));
642     }
643 
644   title_buffer = (char *)calloc(len, sizeof(char));
645   snprintf(title_buffer, len, "%s%s",
646 	       ss->startup_title,
647 	       ((alist->active_sounds > 0) ? ": " : ""));
648 
649   if (alist->active_sounds > 0)
650     {
651       if (alist->active_sounds < 4)
652 	j = alist->active_sounds;
653       else j = 4;
654       for (i = 0; i < j; i++)
655 	{
656 	  strcat(title_buffer, filename_without_directory(alist->names[i]));
657 	  if (i < j - 1)
658 	    strcat(title_buffer, ", ");
659 	}
660       if (alist->active_sounds > 4)
661 	strcat(title_buffer, "...");
662     }
663 
664   set_title(title_buffer);
665   free(title_buffer);
666   free(alist->sounds);
667   free(alist->names);
668   free(alist);
669 }
670 
671 
672 
673 /* -------------------------------- open sound file -------------------------------- */
674 
675 static int fallback_srate = 0, fallback_chans = 0;
676 static mus_sample_t fallback_sample_type = MUS_UNKNOWN_SAMPLE;
677 static int original_srate = 0, original_chans = 0;
678 static mus_sample_t original_sample_type = MUS_UNKNOWN_SAMPLE;
679 
set_fallback_srate(int sr)680 void set_fallback_srate(int sr) {fallback_srate = sr;}
set_fallback_chans(int ch)681 void set_fallback_chans(int ch) {fallback_chans = ch;}
set_fallback_sample_type(mus_sample_t fr)682 void set_fallback_sample_type(mus_sample_t fr) {fallback_sample_type = fr;}
683 
684 
make_file_info_1(const char * fullname)685 static file_info *make_file_info_1(const char *fullname)
686 {
687   file_info *hdr;
688   hdr = (file_info *)calloc(1, sizeof(file_info));
689   hdr->name = mus_strdup(fullname);
690   hdr->type = mus_sound_header_type(fullname);
691   if (hdr->type == MUS_RAW)
692     mus_header_raw_defaults(&(hdr->srate), &(hdr->chans), &(hdr->sample_type));
693   else
694     {
695       hdr->srate = mus_sound_srate(fullname);
696       original_srate = hdr->srate;
697       hdr->chans = mus_sound_chans(fullname);
698       original_chans = hdr->chans;
699       hdr->sample_type = mus_sound_sample_type(fullname);
700       original_sample_type = hdr->sample_type;
701     }
702   if (!(mus_is_sample_type(hdr->sample_type))) hdr->sample_type = fallback_sample_type;
703   if (hdr->srate <= 0) {if (fallback_srate > 0) hdr->srate = fallback_srate; else hdr->srate = 1;}
704   if (hdr->chans <= 0) {if (fallback_chans > 0) hdr->chans = fallback_chans; else hdr->chans = 1;}
705   hdr->samples = mus_sound_samples(fullname); /* total samples, not per channel */
706   if ((hdr->samples == 0) &&
707       (hdr->chans > 128))
708     {
709       snd_warning("no samples, but %d chans?", hdr->chans);
710       hdr->chans = 1;
711     }
712   hdr->data_location = mus_sound_data_location(fullname);
713   hdr->comment = mus_sound_comment(fullname);
714   hdr->loops = mus_sound_loop_info(fullname);
715   return(hdr);
716 }
717 
718 
copy_header(const char * fullname,file_info * ohdr)719 file_info *copy_header(const char *fullname, file_info *ohdr)
720 {
721   file_info *hdr;
722   hdr = (file_info *)calloc(1, sizeof(file_info));
723   hdr->name = mus_strdup(fullname);
724   hdr->comment = NULL;
725   if (ohdr)
726     {
727       hdr->samples = ohdr->samples;
728       hdr->data_location = ohdr->data_location;
729       hdr->srate = ohdr->srate;
730       hdr->chans = ohdr->chans;
731       hdr->sample_type = ohdr->sample_type;
732       hdr->type = ohdr->type;
733       if (ohdr->loops)
734 	{
735 	  int i;
736 	  hdr->loops = (int *)calloc(MUS_LOOP_INFO_SIZE, sizeof(int));
737 	  for (i = 0; i < MUS_LOOP_INFO_SIZE; i++)
738 	    hdr->loops[i] = ohdr->loops[i];
739 	}
740     }
741   return(hdr);
742 }
743 
744 
translate_file(const char * filename,mus_header_t type)745 static file_info *translate_file(const char *filename, mus_header_t type)
746 {
747   file_info *hdr = NULL;
748   char *newname;
749   int *loops = NULL;
750   int err, len, fd;
751 
752   len = strlen(filename);
753   loops = mus_sound_loop_info(filename); /* allocated anew */
754   newname = (char *)calloc(len + 5, sizeof(char));
755   snprintf(newname, len + 5, "%s.snd", filename);
756 
757   /* too many special cases to do anything smart here -- I'll just tack on '.snd' */
758   /* before calling the translator we need to check for write-access to the current directory,
759    * and if none, try to find a temp directory we can write to.
760    *
761    * loop info across translation: 4-Dec-01
762    */
763   fd = CREAT(newname, 0666);
764   if (fd == -1)
765     {
766       char *tempname;
767       tempname = snd_tempnam();
768       fd = CREAT(tempname, 0666);
769       if (fd == -1)
770 	{
771 	  if (loops) free(loops);
772 	  free(newname);
773 	  free(tempname);
774 	  snd_error("can't write translation temp file! (%s)", snd_open_strerror());
775 	  return(NULL);
776 	}
777       free(newname);
778       newname = mus_strdup(tempname);
779       free(tempname);
780     }
781   snd_close(fd, newname);
782   err = snd_translate(filename, newname, type);
783   if (err == MUS_NO_ERROR)
784     {
785       err = mus_header_read(newname);
786       if (err == MUS_NO_ERROR)
787 	{
788 	  hdr = make_file_info_1(newname);
789 	  if (!hdr->loops)
790 	    hdr->loops = loops;
791 	  else
792 	    if (loops) free(loops);
793 	  loops = NULL;
794 	  ss->translated_filename = mus_strdup(newname);
795 	}
796     }
797   else snd_remove(newname, REMOVE_FROM_CACHE);
798   if (newname) free(newname);
799   if (loops) free(loops);
800   return(hdr);
801 }
802 
803 
804 static Xen open_raw_sound_hook;
805 
open_raw_sound(const char * fullname,read_only_t read_only,bool selected)806 static file_info *open_raw_sound(const char *fullname, read_only_t read_only, bool selected)
807 {
808   Xen res = Xen_false;
809   int res_loc = NOT_A_GC_LOC;
810   int srate, chans;
811   mus_sample_t sample_type;
812 
813   if (ss->reloading_updated_file != 0)
814     {
815       /* choices already made, so just send back a header that reflects those choices */
816       return(make_file_info_1(fullname));
817     }
818 
819   if (Xen_hook_has_list(open_raw_sound_hook))
820     {
821 #if HAVE_SCHEME
822       res = s7_call(s7, open_raw_sound_hook, s7_list(s7, 2, C_string_to_Xen_string(fullname), Xen_false)); /* state = #f? */
823 #else
824       Xen arg1, procs;
825       procs = Xen_hook_list(open_raw_sound_hook);
826       arg1 = C_string_to_Xen_string(fullname);
827       while (!Xen_is_null(procs))
828 	{
829 	  res = Xen_call_with_2_args(Xen_car(procs),
830 			   arg1,
831 			   res,
832 			   S_open_raw_sound_hook);
833 	  if (res_loc != NOT_A_GC_LOC) snd_unprotect_at(res_loc);
834 	  res_loc = snd_protect(res);
835 	  procs = Xen_cdr(procs);
836 	}
837 #endif
838     }
839   if (Xen_is_list(res)) /* empty list ok here -> accept all current defaults */
840     {
841       file_info *hdr;
842       mus_long_t data_location, bytes;
843       int len;
844 
845       len = Xen_list_length(res);
846       mus_header_raw_defaults(&srate, &chans, &sample_type);
847       if (len > 0) chans = Xen_integer_to_C_int(Xen_car(res));
848       if (len > 1) srate = Xen_integer_to_C_int(Xen_cadr(res));
849       if (len > 2)
850 	{
851 	  Xen df;
852 	  df = Xen_list_ref(res, 2);
853 	  sample_type = (mus_sample_t)Xen_integer_to_C_int(df);
854 	}
855       if (len > 3) data_location = Xen_llong_to_C_llong(Xen_list_ref(res, 3)); else data_location = 0;
856       if (len > 4) bytes = Xen_llong_to_C_llong(Xen_list_ref(res, 4)); else bytes = mus_sound_length(fullname) - data_location;
857 
858       mus_header_set_raw_defaults(srate, chans, sample_type);
859       mus_sound_override_header(fullname,
860 				srate, chans, sample_type,
861 				MUS_RAW, data_location,
862 				mus_bytes_to_samples(sample_type, bytes));
863 
864       if (res_loc != NOT_A_GC_LOC) snd_unprotect_at(res_loc);
865       hdr = (file_info *)calloc(1, sizeof(file_info));
866       hdr->name = mus_strdup(fullname);
867       hdr->type = MUS_RAW;
868       hdr->srate = mus_sound_srate(fullname);
869       hdr->chans = mus_sound_chans(fullname);
870       hdr->sample_type = mus_sound_sample_type(fullname);
871       hdr->samples = mus_sound_samples(fullname); /* total samples, not per channel */
872       if ((hdr->samples == 0) &&
873 	  (hdr->chans > 8))
874 	{
875 	  snd_warning("no samples, but %d chans?", hdr->chans);
876 	  hdr->chans = 1;
877 	}
878       hdr->data_location = mus_sound_data_location(fullname);
879       hdr->comment = NULL;
880       return(hdr);
881     }
882   else
883     {
884       bool just_quit = false;
885       if (Xen_is_true(res)) just_quit = true;
886       if (res_loc != NOT_A_GC_LOC) snd_unprotect_at(res_loc);
887       if (just_quit) return(NULL);
888 
889       /* open-sound and view-sound do not fall into the raw data dialog */
890       if ((ss->open_requestor == FROM_OPEN_SOUND) ||
891 	  (ss->open_requestor == FROM_VIEW_SOUND) ||
892 	  (ss->open_requestor == FROM_NEW_SOUND)  ||
893 	  (ss->open_requestor == FROM_NEW_FILE_DIALOG))
894 	return(make_file_info_1(fullname));
895 
896 #if (!USE_NO_GUI)
897       {
898 	char *str;
899 	str = (char *)calloc(PRINT_BUFFER_SIZE, sizeof(char));
900 	snprintf(str, PRINT_BUFFER_SIZE, "No header found for %s", filename_without_directory(fullname));
901 	raw_data_dialog_to_file_info(fullname, str, NULL, read_only, selected); /* dialog frees str */
902       }
903 #else
904       fprintf(stderr, "No header found for %s", filename_without_directory(fullname));
905 #endif
906     }
907   return(NULL);
908 }
909 
910 #if (!USE_NO_GUI)
911 static char *raw_data_explanation(const char *filename, file_info *hdr, char **info);
912 #endif
913 
914 
915 static Xen bad_header_hook;
916 
tackle_bad_header(const char * fullname,read_only_t read_only,bool selected)917 static file_info *tackle_bad_header(const char *fullname, read_only_t read_only, bool selected)
918 {
919   /* messed up header */
920   if ((Xen_hook_has_list(bad_header_hook)) &&
921       (Xen_is_true(run_or_hook(bad_header_hook,
922 			      Xen_list_1(C_string_to_Xen_string(fullname)),
923 			      S_bad_header_hook))))
924     return(NULL);
925 
926   /* if not from dialog, throw an error ('bad-header) */
927   if ((ss->open_requestor == FROM_OPEN_SOUND) ||
928       (ss->open_requestor == FROM_VIEW_SOUND) ||
929       (ss->open_requestor == FROM_NEW_SOUND)) /* this case should not happen! */
930     {
931       const char *caller;
932       if (ss->open_requestor == FROM_OPEN_SOUND)
933 	caller = S_open_sound;
934       else caller = S_view_sound;
935       Xen_error(BAD_HEADER,
936 		Xen_list_3(C_string_to_Xen_string("~A: ~S has a bad header?"),
937 			   C_string_to_Xen_string(caller),
938 			   C_string_to_Xen_string(fullname)));
939       return(NULL);
940     }
941 
942 #if (!USE_NO_GUI)
943   {
944     mus_header_t type;
945     type = mus_header_type();
946     if ((type != MUS_MIDI_SAMPLE_DUMP) &&
947 	(type != MUS_IEEE) &&
948 	(type != MUS_MUS10) &&
949 	(type != MUS_HCOM) &&
950 	(!(header_is_encoded(type))))
951       {
952 	char *info = NULL, *title = NULL;
953 	title = raw_data_explanation(fullname, make_file_info_1(fullname), &info);
954 	raw_data_dialog_to_file_info(fullname, title, info, read_only, selected);
955       }
956   }
957 #endif
958   return(NULL);
959 }
960 
961 
make_file_info(const char * fullname,read_only_t read_only,bool selected)962 file_info *make_file_info(const char *fullname, read_only_t read_only, bool selected)
963 {
964   file_info *hdr = NULL;
965   if (mus_file_probe(fullname))
966     {
967       mus_header_t type = MUS_UNKNOWN_HEADER;
968 
969       /* open-raw-sound will force it to viewed as a raw sound */
970       if (ss->open_requestor == FROM_OPEN_RAW_SOUND)
971 	return(make_file_info_1(fullname));
972 
973       type = mus_sound_header_type(fullname);
974       if (type == MUS_UNKNOWN_HEADER)        /* if something went wrong */
975 	type = mus_header_type();            /*    try to read it anyway... */
976 
977       /* handle some files directly through the translator (headers here are not readable) */
978       if ((type == MUS_MIDI_SAMPLE_DUMP) ||
979 	  (type == MUS_IEEE) ||
980 	  (type == MUS_MUS10) ||
981 	  (type == MUS_HCOM) ||
982 	  (header_is_encoded(type)))
983 	{
984 	  return(translate_file(fullname, type));
985 	}
986 
987       if (mus_is_header_type(type))
988 	{ /* at least the header type seems plausible */
989 	  /* check header fields */
990 	  int sr = 0, ch = 0;
991 	  sr = mus_sound_srate(fullname);
992 	  ch = mus_sound_chans(fullname);
993 	  if ((fallback_srate > 0) && ((sr <= 0) || (sr > 100000000))) sr = fallback_srate;
994 	  if ((fallback_chans > 0) && ((ch >= MUS_MAX_CHANS) || (ch <= 0))) ch = fallback_chans;
995 	  if ((sr <= 0) || (sr > 100000000) ||
996 	      (ch >= MUS_MAX_CHANS) || (ch <= 0))
997 	    return(tackle_bad_header(fullname, read_only, selected));
998 
999 	  /* header is ok */
1000 	  if (type == MUS_RAW)
1001 	    return(open_raw_sound(fullname, read_only, selected));
1002 	  else
1003 	    {
1004 	      mus_sample_t sample_type;
1005 	      sample_type = mus_sound_sample_type(fullname);
1006 	      if (mus_is_sample_type(sample_type))
1007 		return(make_file_info_1(fullname));
1008 	      return(translate_file(fullname, type));
1009 	    }
1010 	}
1011       else snd_error("%s does not seem to be a sound file?", fullname); /* no known header */
1012     }
1013   else snd_error("can't find file %s: %s", fullname, snd_io_strerror());
1014   return(hdr);
1015 }
1016 
1017 
make_temp_header(const char * fullname,int srate,int chans,mus_long_t samples,const char * caller)1018 file_info *make_temp_header(const char *fullname, int srate, int chans, mus_long_t samples, const char *caller)
1019 {
1020   file_info *hdr;
1021   hdr = (file_info *)calloc(1, sizeof(file_info));
1022   hdr->name = mus_strdup(fullname);
1023   hdr->samples = samples;
1024   hdr->data_location = 28;
1025   hdr->srate = srate;
1026   hdr->chans = chans;
1027   hdr->sample_type = MUS_OUT_SAMPLE_TYPE;
1028   hdr->type = MUS_NEXT;
1029   /* want direct read/writes for temp files */
1030   hdr->comment = mus_strdup(caller);
1031   return(hdr);
1032 }
1033 
1034 
free_file_info(file_info * hdr)1035 file_info *free_file_info(file_info *hdr)
1036 {
1037   if (hdr)
1038     {
1039       if (hdr->name) free(hdr->name);
1040       if (hdr->comment) free(hdr->comment);
1041       if (hdr->loops) free(hdr->loops);
1042       free(hdr);
1043     }
1044   return(NULL);
1045 }
1046 
1047 
opened_sound_file_name(snd_info * sp)1048 static char *opened_sound_file_name(snd_info *sp)
1049 {
1050   char *newname;
1051   int len;
1052   len = strlen(sp->filename);
1053   newname = (char *)calloc(len + 32, sizeof(char));
1054   snprintf(newname, len + 32, "%s.%s", sp->filename, Xen_file_extension);
1055   return(newname);
1056 }
1057 
1058 
remembered_sound_file_name(snd_info * sp)1059 static char *remembered_sound_file_name(snd_info *sp)
1060 {
1061   char *newname;
1062   int len;
1063   len = strlen(sp->filename);
1064   newname = (char *)calloc(len + 32, sizeof(char));
1065   snprintf(newname, len + 32, "remembered-%s.%s", sp->short_filename, Xen_file_extension);
1066   return(newname);
1067 }
1068 
1069 
load_sound_file_extras(snd_info * sp)1070 static void load_sound_file_extras(snd_info *sp)
1071 {
1072   char *newname;
1073   /* possible sound.scm file */
1074   newname = opened_sound_file_name(sp);
1075   if (file_write_date(newname) >= sp->write_date)
1076       snd_load_file(newname);
1077   free(newname);
1078 
1079   /* remembered-sound.scm -- this is written if remember-sound-state which will also overwrite it */
1080   if (remember_sound_state(ss))
1081     {
1082       newname = remembered_sound_file_name(sp);
1083       if (file_write_date(newname) >= sp->write_date)
1084 	snd_load_file(newname);
1085       free(newname);
1086     }
1087 }
1088 
1089 
remember_sound_file(snd_info * sp)1090 static void remember_sound_file(snd_info *sp)
1091 {
1092   char *newname;
1093   FILE *fd;
1094 
1095   newname = remembered_sound_file_name(sp);
1096   fd = FOPEN(newname, "w");
1097 
1098   if (!fd)
1099     {
1100       snd_error("remember sound state can't write %s: %s", newname, snd_io_strerror());
1101       return;
1102     }
1103   sp->remembering = true;
1104   save_sound_state(sp, fd);
1105   sp->remembering = false;
1106   snd_fclose(fd, newname);
1107   free(newname);
1108 }
1109 
1110 
1111 static Xen snd_opened_sound;
1112 static Xen open_hook;
1113 static Xen close_hook;
1114 static Xen before_close_hook;
1115 static Xen during_open_hook;
1116 static Xen after_open_hook;
1117 
during_open(int fd,const char * file,open_reason_t reason)1118 void during_open(int fd, const char *file, open_reason_t reason)
1119 {
1120   if (Xen_hook_has_list(during_open_hook))
1121     run_hook(during_open_hook,
1122 	     Xen_list_3(C_int_to_Xen_integer(fd),
1123 			C_string_to_Xen_string(file),
1124 			C_int_to_Xen_integer((int)reason)),
1125 	     S_during_open_hook);
1126 }
1127 
1128 
after_open(snd_info * sp)1129 void after_open(snd_info *sp)
1130 {
1131   /* the sync-style choice used to be handled via after-open-hook in extensions.*, but 15-Feb-11 has
1132    *   been moved into the main Snd.  So here is the code...
1133    */
1134   if (sp->nchans > 1)
1135     {
1136       switch (sync_style(ss))
1137 	{
1138 	case SYNC_NONE:
1139 	  sp->sync = 0;
1140 	  break;
1141 
1142 	case SYNC_ALL:
1143 	  sp->sync = 1;
1144 	  break;
1145 
1146 	case SYNC_BY_SOUND:
1147 	  ss->sound_sync_max++;
1148 	  sp->sync = ss->sound_sync_max; /* if we had used (set! (sync) ...) this would be set (via syncb) to the new max */
1149 	  break;
1150 	}
1151     }
1152   else sp->sync = 0;
1153   syncb(sp, sp->sync);
1154 
1155   if (Xen_hook_has_list(after_open_hook))
1156     run_hook(after_open_hook,
1157 	     Xen_list_1(C_int_to_Xen_sound(sp->index)),
1158 	     S_after_open_hook);
1159 
1160   if (Xen_hook_has_list(ss->snd_open_file_hook))
1161     run_hook(ss->snd_open_file_hook,
1162 	     Xen_list_1(C_int_to_Xen_integer(FILE_OPENED)),
1163 	     "open-file-hook");
1164 
1165   if (Xen_hook_has_list(ss->effects_hook))
1166     run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
1167 }
1168 
1169 
finish_opening_sound(snd_info * sp,bool selected)1170 snd_info *finish_opening_sound(snd_info *sp, bool selected)
1171 {
1172   if (sp)
1173     {
1174 #if HAVE_RUBY || HAVE_FORTH
1175       Xen_variable_set(S_snd_opened_sound, C_int_to_Xen_sound(sp->index));
1176 #endif
1177 
1178 #if HAVE_SCHEME
1179       Xen_variable_set(snd_opened_sound, C_int_to_Xen_sound(sp->index));
1180 #endif
1181       sp->write_date = file_write_date(sp->filename); /* redundant (see snd-xsnd.c) */
1182       sp->need_update = false;
1183       ss->active_sounds++;
1184       reflect_file_change_in_title();
1185       monitor_sound(sp);
1186     }
1187   for_each_separate_chan(channel_open_pane);
1188 
1189 #if USE_MOTIF
1190   for_each_separate_chan(channel_unlock_pane);
1191 #endif
1192 
1193   if (sp)
1194     {
1195       add_srate_to_completion_list(snd_srate(sp));
1196       if ((selected) &&
1197 	  (sp->active) &&
1198 	  (sp->inuse == SOUND_NORMAL))
1199 	select_channel(sp, 0);
1200       load_sound_file_extras(sp);
1201 
1202       if ((sp->channel_style != CHANNELS_SEPARATE) &&
1203 	  (sp->nchans > 1))
1204 	{
1205 	  channel_style_t val;
1206 	  val = sp->channel_style;
1207 	  sp->channel_style = CHANNELS_SEPARATE;
1208 	  if (val == CHANNELS_COMBINED)
1209 	    combine_sound(sp);
1210 	  else superimpose_sound(sp);
1211 	}
1212     }
1213   return(sp);
1214 }
1215 
1216 
1217 /* (hook-push open-hook (lambda (f) (display f) #f)) */
1218 
snd_open_file(const char * filename,read_only_t read_only)1219 snd_info *snd_open_file(const char *filename, read_only_t read_only)
1220 {
1221   file_info *hdr;
1222   snd_info *sp;
1223   static char *mcf = NULL;
1224 
1225   if (mcf) free(mcf);
1226   mcf = mus_expand_filename(filename);
1227   if (Xen_hook_has_list(open_hook))
1228     {
1229       Xen res, fstr;
1230       fstr = C_string_to_Xen_string(mcf);
1231       res = run_or_hook(open_hook,
1232 			Xen_list_1(fstr),
1233 			S_open_hook);
1234       if (Xen_is_true(res))
1235 	{
1236 	  if (mcf) {free(mcf); mcf = NULL;}
1237 	  return(NULL);
1238 	}
1239       else
1240 	{
1241 	  if (Xen_is_string(res))  /* added 14-Aug-01 for user-supplied auto-translations */
1242 	    {
1243 	      if (mcf) free(mcf);
1244 	      mcf = mus_expand_filename(Xen_string_to_C_string(res));
1245 	    }
1246 	}
1247     }
1248 
1249   hdr = make_file_info(mcf, read_only, FILE_SELECTED);
1250   if (!hdr)
1251     {
1252       if (mcf) {free(mcf); mcf = NULL;}
1253       return(NULL);
1254     }
1255 
1256   sp = add_sound_window(mcf, read_only, hdr);
1257 
1258   if (mcf) {free(mcf); mcf = NULL;}
1259   return(finish_opening_sound(sp, FILE_SELECTED));
1260 }
1261 
1262 
snd_close_file(snd_info * sp)1263 void snd_close_file(snd_info *sp)
1264 {
1265   Xen res = Xen_false;
1266   snd_info *chosen_sp = NULL;
1267 
1268   /* before-close-hook can cancel the close, whereas close-hook can't */
1269   if (Xen_hook_has_list(before_close_hook))
1270     res = run_or_hook(before_close_hook,
1271 		      Xen_list_1(C_int_to_Xen_sound(sp->index)),
1272 		      S_before_close_hook);
1273   if (Xen_is_true(res)) return;
1274 
1275 #if (!USE_NO_GUI)
1276   if ((ask_about_unsaved_edits(ss)) &&
1277       (has_unsaved_edits(sp)))
1278     {
1279       save_edits_now(sp);
1280       return;
1281     }
1282   unpost_unsaved_edits_if_any(sp);
1283   unpost_file_has_changed_if_any(sp);
1284 #endif
1285 
1286   if (peak_env_dir(ss))
1287     map_over_sound_chans(sp, write_peak_env_info_file);
1288 
1289   if (Xen_hook_has_list(close_hook))
1290     run_hook(close_hook,
1291 	     Xen_list_1(C_int_to_Xen_sound(sp->index)),
1292 	     S_close_hook);
1293 
1294   if (remember_sound_state(ss))
1295     remember_sound_file(sp);
1296 
1297   remember_filename(sp->filename, preloaded_files); /* for open dialog(s) previous files list and File:open recent files menu */
1298 
1299   /* an experiment -- event queue seems to be glomming up when lots of fast open/close,
1300    * but squelch updates just in case a redisplay event is in the queue.  But check_for_event
1301    * here is dangerous because a channel might be closed and deallocated already, then
1302    * this check lets a mouse event through, cp->axis is NULL, cp->active has been stomped on,
1303    * segfault!
1304    */
1305 
1306   {
1307     uint32_t i;
1308     for (i = 0; i < sp->nchans; i++)
1309       sp->chans[i]->squelch_update = true;
1310     /* check_for_event(); */
1311 
1312     sp->file_watcher = unmonitor_file(sp->file_watcher);
1313 
1314     /* exit does not go through this function to clean up temps -- see snd_exit_cleanly in snd-main.c */
1315     if (selection_creation_in_progress()) finish_selection_creation();
1316 
1317     if (ss->deferred_regions > 0)
1318       for (i = 0; i < sp->nchans; i++)
1319 	if (sp->chans[i])
1320 	  sequester_deferred_regions(sp->chans[i], -1);
1321   }
1322 
1323   sp->inuse = SOUND_IDLE;
1324   if (sp->playing)
1325     stop_playing_sound(sp, PLAY_CLOSE);
1326 
1327 #if (!USE_NO_GUI)
1328   sp->inuse = SOUND_NORMAL;               /* needed to make sure the status area is actually cleared in set_status */
1329   set_status(sp, NULL, false); /* false = don't try to update graphs! */
1330   sp->inuse = SOUND_IDLE;
1331 #endif
1332 
1333   if ((sp == selected_sound()) &&
1334       (!(ss->exiting)))
1335     {
1336       int i, curmax = -1;
1337       /* look for the last selected sound, if any */
1338       for (i = 0; i < ss->max_sounds; i++)
1339 	{
1340 	  snd_info *nsp;
1341 	  nsp = ss->sounds[i];
1342 	  if ((nsp) &&
1343 	      (nsp->inuse == SOUND_NORMAL) &&
1344 	      (nsp != sp) &&
1345 	      (nsp->active) &&
1346 	      (nsp->selectpos > curmax))
1347 	    {
1348 	      curmax = nsp->selectpos;
1349 	      chosen_sp = nsp;
1350 	    }
1351 	}
1352     }
1353 
1354   /* if sequester_deferred_regions is in free_snd_info (moved up to this level 15-12-03)
1355    *   If the sound is set to SOUND_IDLE, the init function returns 'no-such-sound, and the
1356    *   subsequent read segfaults.
1357    */
1358   free_snd_info(sp);
1359 
1360   ss->active_sounds--;
1361   reflect_file_change_in_title();
1362   enved_reflect_selection(selection_is_active());
1363   reflect_selection_in_save_as_dialog(selection_is_active());
1364 
1365   if (Xen_hook_has_list(ss->snd_open_file_hook))
1366     run_hook(ss->snd_open_file_hook,
1367 	     Xen_list_1(C_int_to_Xen_integer(FILE_CLOSED)),
1368 	     "open-file-hook");
1369 
1370   if (Xen_hook_has_list(ss->effects_hook))
1371     run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
1372 
1373   if (chosen_sp)
1374     select_channel(chosen_sp, 0);
1375   else
1376     {
1377       if (sp == selected_sound())
1378 	ss->selected_sound = NO_SELECTION;
1379 
1380       if ((!(ss->exiting)) &&
1381 	  (!any_selected_sound())) /* I hope this can't be fooled... */
1382 	reset_mix_ctr();
1383     }
1384 
1385   reflect_save_as_sound_selection(NULL);
1386 }
1387 
1388 
1389 #define TEMP_SOUND_INDEX 123456
1390 /* just a marker for debugging -- but it actually appears in things like start-playing-hook!
1391  */
1392 
1393 
make_sound_readable(const char * filename,bool post_close)1394 snd_info *make_sound_readable(const char *filename, bool post_close)
1395 {
1396   /* conjure up just enough Snd structure to make this sound readable by the edit-tree readers */
1397   snd_info *sp;
1398   chan_info *cp;
1399   file_info *hdr = NULL;
1400   int i;
1401   mus_long_t len;
1402   /* we've already checked that filename exists */
1403 
1404   hdr = make_file_info_1(filename);
1405   sp = make_basic_snd_info(hdr->chans);
1406   sp->nchans = hdr->chans;
1407   sp->hdr = hdr;
1408   sp->inuse = SOUND_READER;
1409   initialize_control_panel(sp);
1410   sp->index = TEMP_SOUND_INDEX;
1411   len = (hdr->samples) / (hdr->chans);
1412 
1413   for (i = 0; i < (int)sp->nchans; i++)
1414     {
1415       int fd;
1416       cp = make_chan_info(NULL, i, sp);
1417       cp->editable = false;
1418       free(cp->ax);
1419       cp->ax = NULL;
1420       sp->chans[i] = cp;
1421       add_channel_data_1(cp, hdr->srate, len, WITHOUT_GRAPH);
1422 
1423       fd = snd_open_read(filename); /* sends the error if any */
1424       if (fd != -1)
1425 	set_up_snd_io(cp, i, fd, filename, hdr, post_close);
1426     }
1427   sp->active = true;
1428   return(sp);
1429 }
1430 
1431 
free_axes_data(axes_data * sa)1432 axes_data *free_axes_data(axes_data *sa)
1433 {
1434   if (sa)
1435     {
1436       if (sa->axis_data) {free(sa->axis_data); sa->axis_data = NULL;}
1437       if (sa->fftp) {free(sa->fftp); sa->fftp = NULL;}
1438       if (sa->wavep) {free(sa->wavep); sa->wavep = NULL;}
1439       free(sa);
1440     }
1441   return(NULL);
1442 }
1443 
1444 
1445 enum {SA_X0, SA_X1, SA_Y0, SA_Y1, SA_XMIN, SA_XMAX, SA_YMIN, SA_YMAX, SA_ZX, SA_ZY, SA_SX, SA_SY, SA_GSY, SA_GZY};
1446 #define SA_FIELDS 14
1447 
make_axes_data(snd_info * sp)1448 axes_data *make_axes_data(snd_info *sp)
1449 {
1450   axes_data *sa;
1451   int i;
1452   sa = (axes_data *)calloc(1, sizeof(axes_data));
1453   sa->chans = sp->nchans;
1454   sa->fields = SA_FIELDS;
1455   sa->axis_data = (double *)calloc(sa->fields * sa->chans, sizeof(double));
1456   sa->fftp = (bool *)calloc(sa->chans, sizeof(bool));
1457   sa->wavep = (bool *)calloc(sa->chans, sizeof(bool));
1458   for (i = 0; i < sa->chans; i++)
1459     {
1460       chan_info *cp;
1461       axis_info *ap;
1462       int loc;
1463       cp = sp->chans[i];
1464       ap = cp->axis;
1465       loc = i * sa->fields;
1466       sa->axis_data[loc + SA_X0] = ap->x0;
1467       sa->axis_data[loc + SA_X1] = ap->x1;
1468       sa->axis_data[loc + SA_Y0] = ap->y0;
1469       sa->axis_data[loc + SA_Y1] = ap->y1;
1470       sa->axis_data[loc + SA_XMIN] = ap->xmin;
1471       sa->axis_data[loc + SA_XMAX] = ap->xmax;
1472       sa->axis_data[loc + SA_YMIN] = ap->ymin;
1473       sa->axis_data[loc + SA_YMAX] = ap->ymax;
1474       sa->axis_data[loc + SA_ZX] = ap->zx;
1475       sa->axis_data[loc + SA_SX] = ap->sx;
1476       sa->axis_data[loc + SA_ZY] = ap->zy;
1477       sa->axis_data[loc + SA_SY] = ap->sy;
1478       sa->axis_data[loc + SA_GSY] = cp->gsy;
1479       sa->axis_data[loc + SA_GZY] = cp->gzy;
1480       sa->wavep[i] = cp->graph_time_on;
1481       sa->fftp[i] = cp->graph_transform_on;
1482 
1483       /* unite and sync buttons are being cleared in snd_info_cleanup and explicitly in snd_update
1484        *   then probably reset in change_channel_style
1485        *   meanwhile channel_style is saved in copy_snd_info below
1486        *   but also restored explicitly in snd_update.
1487        *   but... if unite was off  before the update, it seems that it is set on multi-channel files
1488        *    (via channel_style(ss) which defaults to channels_combined)
1489        */
1490     }
1491   return(sa);
1492 }
1493 
1494 
restore_axes_data(snd_info * sp,axes_data * sa,mus_float_t new_duration,bool need_edit_history_update)1495 void restore_axes_data(snd_info *sp, axes_data *sa, mus_float_t new_duration, bool need_edit_history_update)
1496 {
1497   int i, j;
1498   for (i = 0, j = 0; i < (int)sp->nchans; i++)
1499     {
1500       chan_info *cp;
1501       axis_info *ap;
1502       int loc;
1503 
1504       cp = sp->chans[i];
1505       loc = j * sa->fields;
1506 
1507       if ((show_full_duration(ss)) &&
1508 	  (sa->axis_data[loc + SA_X0] == 0.0) &&
1509 	  (sa->axis_data[loc + SA_X1] == sa->axis_data[loc + SA_XMAX]))
1510 	{
1511 	  /* we were viewing the full duration when the update occurred, and show-full-duration is #t,
1512 	   *   so show the full new duration.
1513 	   */
1514 	  sa->axis_data[loc + SA_X1] = new_duration;                             /* new x1 */
1515 	}
1516       else
1517 	{
1518 	  mus_float_t old_duration;                                            /* old duration is x1 - x0 */
1519 	  old_duration = sa->axis_data[loc + SA_X1] - sa->axis_data[loc + SA_X0];
1520 	  if (new_duration < sa->axis_data[loc + SA_X0])                       /* new duration < old x0 */
1521 	    sa->axis_data[loc + SA_X0] = new_duration - old_duration;          /* try to maintain old window size */
1522 	  if (sa->axis_data[loc + SA_X0] < 0.0)
1523 	    sa->axis_data[loc + SA_X0] = 0.0;
1524 	  if (new_duration < sa->axis_data[loc + SA_X1])
1525 	    sa->axis_data[loc + SA_X1] = new_duration;                         /* new x1 */
1526 	}
1527       sa->axis_data[loc + SA_XMAX] = new_duration;                             /* new xmax */
1528 
1529       ap = cp->axis;
1530       ap->xmin = sa->axis_data[loc + SA_XMIN];
1531       ap->xmax = sa->axis_data[loc + SA_XMAX];
1532       /* zx and sx are reset by set_axes below? */
1533       ap->zx = sa->axis_data[loc + SA_ZX];
1534       ap->sx = sa->axis_data[loc + SA_SX];
1535 
1536       ap->x_ambit = ap->xmax - ap->xmin;
1537 
1538       if (!show_full_range(ss))
1539 	{
1540 	  ap->ymin = sa->axis_data[loc + SA_YMIN];
1541 	  ap->ymax = sa->axis_data[loc + SA_YMAX];
1542 	  ap->zy = sa->axis_data[loc + SA_ZY];
1543 	  ap->sy = sa->axis_data[loc + SA_SY];
1544 	  ap->y_ambit = ap->ymax - ap->ymin;
1545 	}
1546       else
1547 	{
1548 	  sa->axis_data[loc + SA_Y0] = ap->y0;
1549 	  sa->axis_data[loc + SA_Y1] = ap->y1;
1550 	}
1551 
1552       cp->gzy = sa->axis_data[loc + SA_GZY];
1553       cp->gsy = sa->axis_data[loc + SA_GSY];
1554 
1555       set_axes(cp,
1556 	       sa->axis_data[loc + SA_X0],
1557 	       sa->axis_data[loc + SA_X1],
1558 	       sa->axis_data[loc + SA_Y0],
1559 	       sa->axis_data[loc + SA_Y1]);
1560 
1561       set_z_scrollbars(cp, ap);
1562 
1563       if ((cp->chan == 0) &&
1564 	  (cp->gzy != 1.0))
1565 	change_gzy(cp->gzy, cp); /* also fixes gsy slider */
1566 
1567       update_graph(cp); /* get normalized state before messing with it */
1568 
1569       if (sa->fftp[j])
1570 	fftb(cp, true);
1571 
1572       if (!(sa->wavep[j]))
1573 	waveb(cp, false);
1574 
1575       if (need_edit_history_update)
1576 	reflect_edit_history_change(cp);
1577 
1578       if (j < (sa->chans - 1)) j++;
1579     }
1580 }
1581 
1582 
copy_chan_info(chan_info * ncp,chan_info * ocp)1583 static void copy_chan_info(chan_info *ncp, chan_info *ocp)
1584 {
1585   ncp->cursor_on = ocp->cursor_on;
1586   ncp->cursor_style = ocp->cursor_style;
1587   ncp->tracking_cursor_style = ocp->tracking_cursor_style;
1588   ncp->cursor_size = ocp->cursor_size;
1589   ncp->spectro_x_scale = ocp->spectro_x_scale;
1590   ncp->spectro_y_scale = ocp->spectro_y_scale;
1591   ncp->spectro_z_scale = ocp->spectro_z_scale;
1592   ncp->spectro_z_angle = ocp->spectro_z_angle;
1593   ncp->spectro_x_angle = ocp->spectro_x_angle;
1594   ncp->spectro_y_angle = ocp->spectro_y_angle;
1595   ncp->spectrum_end = ocp->spectrum_end;
1596   ncp->spectrum_start = ocp->spectrum_start;
1597   ncp->lin_dB = ocp->lin_dB;
1598   ncp->min_dB = ocp->min_dB;
1599   ncp->fft_window_alpha = ocp->fft_window_alpha;
1600   ncp->fft_window_beta = ocp->fft_window_beta;
1601   ncp->beats_per_minute = ocp->beats_per_minute;
1602   ncp->beats_per_measure = ocp->beats_per_measure;
1603   ncp->show_y_zero = ocp->show_y_zero;
1604   ncp->show_grid = ocp->show_grid;
1605   ncp->grid_density = ocp->grid_density;
1606   ncp->show_sonogram_cursor = ocp->show_sonogram_cursor;
1607   ncp->show_marks = ocp->show_marks;
1608   ncp->wavo_hop = ocp->wavo_hop;
1609   ncp->wavo_trace = ocp->wavo_trace;
1610   ncp->zero_pad = ocp->zero_pad;
1611   ncp->x_axis_style = ocp->x_axis_style;
1612   ncp->wavelet_type = ocp->wavelet_type;
1613   ncp->with_verbose_cursor = ocp->with_verbose_cursor;
1614   ncp->max_transform_peaks = ocp->max_transform_peaks;
1615   ncp->show_transform_peaks = ocp->show_transform_peaks;
1616   ncp->show_axes = ocp->show_axes;
1617   ncp->time_graph_style = ocp->time_graph_style;
1618   ncp->lisp_graph_style = ocp->lisp_graph_style;
1619   ncp->transform_graph_style = ocp->transform_graph_style;
1620   ncp->fft_log_frequency = ocp->fft_log_frequency;
1621   ncp->fft_log_magnitude = ocp->fft_log_magnitude;
1622   ncp->transform_size = ocp->transform_size;
1623   ncp->transform_graph_type = ocp->transform_graph_type;
1624   ncp->fft_window = ocp->fft_window;
1625   ncp->time_graph_type = ocp->time_graph_type;
1626   ncp->dot_size = ocp->dot_size;
1627   ncp->transform_normalization = ocp->transform_normalization;
1628   ncp->transform_type = ocp->transform_type;
1629   ncp->show_mix_waveforms = ocp->show_mix_waveforms;
1630   ncp->spectro_hop = ocp->spectro_hop;
1631   ncp->graphs_horizontal = ocp->graphs_horizontal;
1632   ncp->cursor_proc = ocp->cursor_proc;
1633   if (Xen_is_bound(ncp->cursor_proc))
1634     ncp->cursor_proc_loc = snd_protect(ncp->cursor_proc);
1635   else ncp->cursor_proc_loc = NOT_A_GC_LOC;
1636 }
1637 
1638 
copy_snd_info(snd_info * nsp,snd_info * osp)1639 static void copy_snd_info(snd_info *nsp, snd_info *osp)
1640 {
1641   nsp->speed_control_style = osp->speed_control_style;
1642   nsp->speed_control_tones = osp->speed_control_tones;
1643   nsp->expand_control_length = osp->expand_control_length;
1644   nsp->expand_control_ramp = osp->expand_control_ramp;
1645   nsp->expand_control_hop = osp->expand_control_hop;
1646   nsp->expand_control_jitter = osp->expand_control_jitter;
1647   nsp->contrast_control_amp = osp->contrast_control_amp;
1648   nsp->reverb_control_feedback = osp->reverb_control_feedback;
1649   nsp->reverb_control_lowpass = osp->reverb_control_lowpass;
1650   nsp->reverb_control_decay = osp->reverb_control_decay;
1651   nsp->channel_style = osp->channel_style;
1652   nsp->sync = osp->sync;
1653 }
1654 
1655 
sound_store_chan_info(snd_info * sp)1656 static snd_info *sound_store_chan_info(snd_info *sp)
1657 {
1658   chan_info **cps;
1659   snd_info *nsp;
1660   uint32_t i;
1661 
1662   nsp = (snd_info *)calloc(1, sizeof(snd_info));
1663   cps = (chan_info **)calloc(sp->nchans, sizeof(chan_info *));
1664   nsp->chans = cps;
1665   nsp->nchans = sp->nchans;
1666   copy_snd_info(nsp, sp);
1667   for (i = 0; i < sp->nchans; i++)
1668     {
1669       cps[i] = (chan_info *)calloc(1, sizeof(chan_info));
1670       copy_chan_info(cps[i], sp->chans[i]);
1671     }
1672   return(nsp);
1673 }
1674 
1675 
sound_restore_chan_info(snd_info * nsp,snd_info * osp)1676 static void sound_restore_chan_info(snd_info *nsp, snd_info *osp)
1677 {
1678   uint32_t i;
1679   chan_info **cps;
1680 
1681   cps = osp->chans;
1682   copy_snd_info(nsp, osp);
1683   for (i = 0; i < nsp->nchans; i++)
1684     {
1685       copy_chan_info(nsp->chans[i], cps[i]);
1686       if (Xen_is_bound(cps[i]->cursor_proc))
1687 	{
1688 	  snd_unprotect_at(cps[i]->cursor_proc_loc);
1689 	  cps[i]->cursor_proc = Xen_undefined;
1690 	  cps[i]->cursor_proc_loc = NOT_A_GC_LOC;
1691 	}
1692     }
1693 }
1694 
1695 
1696 static Xen update_hook;
1697 
snd_update(snd_info * sp)1698 snd_info *snd_update(snd_info *sp)
1699 {
1700   /* we can't be real smart here because the channel number may have changed and so on */
1701   int i, old_srate, old_chans, sp_chans, old_index, gc_loc = NOT_A_GC_LOC, old_selected_channel = NO_SELECTION;
1702   mus_sample_t old_sample_type;
1703   channel_style_t old_channel_style;
1704   read_only_t read_only;
1705   bool old_raw;
1706   axes_data *sa;
1707   snd_info *nsp = NULL;
1708   char *filename;
1709   void *ms;
1710   snd_info *saved_sp;
1711   struct ctrl_state *saved_controls;
1712   mus_long_t *old_cursors;
1713   void *old_file_watcher;
1714   Xen update_hook_result = Xen_false;
1715   const char *ur_filename;
1716   int app_x, app_y;
1717 
1718 #if USE_MOTIF
1719   int snd_height;
1720   snd_height = snd_pane_height(sp);
1721 #endif
1722 
1723   if (sp->edited_region) return(sp);
1724   if ((sp->inuse != SOUND_NORMAL) ||
1725       (!(sp->filename)))
1726     return(sp);
1727 
1728   if (mus_file_probe(sp->filename) == 0)
1729     {
1730       snd_error("%s no longer exists!", sp->short_filename);
1731       return(sp);
1732     }
1733 
1734   app_x = widget_width(main_shell(ss));
1735   app_y = widget_height(main_shell(ss));
1736   ur_filename = sp->filename;
1737 
1738   if (peak_env_dir(ss))
1739     for_each_sound_chan(sp, delete_peak_env_info_file);
1740 
1741   if (with_inset_graph(ss))
1742     for_each_sound_chan(sp, clear_inset_graph);
1743 
1744   if (Xen_hook_has_list(update_hook))
1745     {
1746       /* #t => return without updating (not recommended!!), proc of 1 arg will be evaluated after update is complete */
1747       update_hook_result = run_or_hook(update_hook,
1748 				       Xen_list_1(C_int_to_Xen_sound(sp->index)),
1749 				       S_update_hook);
1750       if (Xen_is_true(update_hook_result)) return(sp);
1751       if (Xen_is_procedure(update_hook_result))
1752 	{
1753 	  if (Xen_is_aritable(update_hook_result, 1))
1754 	    gc_loc = snd_protect(update_hook_result);
1755 	  else Xen_bad_arity_error(S_update_hook, 0, update_hook_result, S_update_hook " function result should require 1 arg");
1756 	}
1757     }
1758 
1759   filename = mus_strdup(ur_filename);
1760   read_only = sp->user_read_only;
1761   if (sp->nchans > 1)
1762     ss->update_sound_channel_style = sp->channel_style;
1763   sa = make_axes_data(sp);
1764   old_raw = (sp->hdr->type == MUS_RAW);
1765   if (old_raw)
1766     {
1767       mus_header_raw_defaults(&old_srate, &old_chans, &old_sample_type);
1768       mus_header_set_raw_defaults(sp->hdr->srate, sp->hdr->chans, sp->hdr->sample_type);
1769     }
1770 
1771   if (sp == selected_sound())
1772     old_selected_channel = sp->selected_channel;
1773 
1774   sp_chans = sp->nchans;
1775   old_index = sp->index;
1776   old_channel_style = sp->channel_style;
1777   if (sp->channel_style != CHANNELS_SEPARATE)
1778     set_sound_channel_style(sp, CHANNELS_SEPARATE);
1779 
1780   ms = (void *)sound_store_marks(sp);
1781   save_controls(sp);
1782   saved_controls = sp->saved_controls;
1783   sp->saved_controls = NULL;
1784   saved_sp = sound_store_chan_info(sp);
1785   old_cursors = (mus_long_t *)calloc(sp_chans, sizeof(mus_long_t));
1786 
1787   /* peak-env code saves the current peak-envs on exit (snd_close), but in this case, that
1788    *   data is known to be out-of-date.  Since we'll be freeing it eventually anyway, we
1789    *   do it first here, and the peak-env update-hook clobbers the existing files
1790    */
1791   for (i = 0; i < sp_chans; i++)
1792     {
1793       chan_info *ncp;
1794       int k;
1795       ncp = sp->chans[i];
1796       old_cursors[i] = cursor_sample(ncp);
1797       for (k = 0; k < ncp->edit_size; k++)
1798 	{
1799 	  ed_list *ed;
1800 	  ed = ncp->edits[k];
1801 	  if (ed)
1802 	    ed->peak_env = free_peak_env(ncp, k);
1803 	}
1804     }
1805   old_file_watcher = sp->file_watcher; /* will be unmonitored in snd_close_file, but we need to know if it is being monitored now */
1806 
1807   snd_close_file(sp);
1808 
1809   /* no mus_sound_forget here because we may be simply re-interpreting the existing data (set! (sample-type) ...) etc */
1810   /* this normalizes the fft/lisp/wave state so we need to reset it after reopen */
1811 
1812 #if USE_MOTIF
1813   if (!(ss->file_monitor_ok))
1814     alert_new_file();
1815 #endif
1816 
1817   ss->reloading_updated_file = (old_index + 1);
1818   ss->open_requestor = FROM_UPDATE;
1819 
1820   nsp = snd_open_file(filename, read_only);
1821 
1822 #if USE_MOTIF
1823   XtVaSetValues(w_snd_pane(sp),
1824 		    XmNpaneMinimum, snd_height,
1825 		    XmNpaneMaximum, snd_height,
1826 		    NULL);
1827 #endif
1828   set_widget_size(main_shell(ss), app_x, app_y); /* was at end */
1829 
1830   ss->reloading_updated_file = 0;
1831   if (old_raw)
1832     mus_header_set_raw_defaults(old_srate, old_chans, old_sample_type);
1833   if (nsp)
1834     {
1835       /* if header is bad, nsp can be null awaiting raw data dialog's return */
1836 
1837       if ((old_file_watcher) &&
1838 	  (!(nsp->file_watcher)))
1839 	monitor_sound(nsp);
1840       /* might be a different sp as well as underlying file */
1841 
1842       nsp->saved_controls = saved_controls;
1843       if (saved_controls) restore_controls(nsp);
1844       if ((int)nsp->nchans == sp_chans) sound_restore_chan_info(nsp, saved_sp);
1845 
1846       if ((old_selected_channel != NO_SELECTION) &&
1847 	  (old_selected_channel < (int)nsp->nchans) &&
1848 	  (nsp == selected_sound()))
1849 	select_channel(nsp, old_selected_channel);
1850 
1851       restore_axes_data(nsp, sa, mus_sound_duration(filename), false);
1852       sound_restore_marks(nsp, ms);
1853 
1854       for (i = 0; (i < (int)nsp->nchans) && (i < sp_chans); i++)
1855 	cursor_sample(nsp->chans[i]) = old_cursors[i];
1856 
1857       if ((nsp->nchans > 1) &&
1858 	  (old_channel_style != CHANNELS_SEPARATE)) /* we set it to separate before the update */
1859 	set_sound_channel_style(nsp, old_channel_style);
1860     }
1861 
1862   free(old_cursors);
1863 
1864   if (Xen_is_procedure(update_hook_result))
1865     {
1866       Xen_call_with_1_arg(update_hook_result,
1867 		 (nsp) ? C_int_to_Xen_sound(nsp->index) : Xen_false,
1868 		 "procedure returned by " S_update_hook);
1869       if (gc_loc != NOT_A_GC_LOC) snd_unprotect_at(gc_loc);
1870     }
1871 
1872   if (saved_sp)
1873     {
1874       for (i = 0; i < (int)saved_sp->nchans; i++)
1875 	if (saved_sp->chans[i]) free(saved_sp->chans[i]);
1876       free(saved_sp->chans);
1877       free(saved_sp);
1878     }
1879   free_axes_data((axes_data *)sa);
1880   free(filename);
1881 
1882 #if USE_MOTIF
1883   XtVaSetValues(w_snd_pane(sp),
1884 		    XmNpaneMinimum, 1,
1885 		    XmNpaneMaximum, LOTSA_PIXELS,
1886 		    NULL);
1887 #endif
1888 
1889   return(nsp);
1890 }
1891 
1892 
snd_update_warning_handler(const char * msg,void * data)1893 static void snd_update_warning_handler(const char *msg, void *data)
1894 {
1895   set_status((snd_info *)data, msg, false);
1896 }
1897 
1898 
snd_update_error_handler(const char * msg,void * data)1899 static void snd_update_error_handler(const char *msg, void *data)
1900 {
1901   redirect_snd_error_to(NULL, NULL);
1902   redirect_snd_warning_to(NULL, NULL);
1903   Xen_error(Xen_make_error_type("cant-update-file"),
1904 	    Xen_list_3(C_string_to_Xen_string("~A: ~A"),
1905 		       C_string_to_Xen_string((char *)data),
1906 		       C_string_to_Xen_string(msg)));
1907 }
1908 
1909 
snd_update_within_xen(snd_info * sp,const char * caller)1910 snd_info *snd_update_within_xen(snd_info *sp, const char *caller)
1911 {
1912   snd_info *nsp;
1913   redirect_snd_error_to(snd_update_error_handler, (void *)caller);
1914   redirect_snd_warning_to(snd_update_warning_handler, (void *)sp);
1915   nsp = snd_update(sp);
1916   redirect_snd_error_to(NULL, NULL);
1917   redirect_snd_warning_to(NULL, NULL);
1918   return(nsp);
1919 }
1920 
1921 
1922 static Xen after_save_as_hook;
1923 
run_after_save_as_hook(snd_info * sp,const char * already_saved_as_name,bool from_save_as_dialog)1924 void run_after_save_as_hook(snd_info *sp, const char *already_saved_as_name, bool from_save_as_dialog)
1925 {
1926   /* might be save-selection, as well as save-sound-as */
1927   if (Xen_hook_has_list(after_save_as_hook))
1928     {
1929       char *fullname;
1930       fullname = mus_expand_filename(already_saved_as_name);
1931       run_progn_hook(after_save_as_hook,
1932 		     Xen_list_3((sp) ? C_int_to_Xen_sound(sp->index) : Xen_false,
1933 				C_string_to_Xen_string(fullname),
1934 				C_bool_to_Xen_boolean(from_save_as_dialog)),
1935 		     S_after_save_as_hook);
1936       free(fullname);
1937     }
1938 }
1939 
1940 
1941 static Xen before_save_as_hook;
1942 static bool before_save_as_hook_active = false;
1943 
run_before_save_as_hook(snd_info * sp,const char * save_as_filename,bool selection,int srate,mus_sample_t smp_type,mus_header_t hd_type,const char * comment)1944 bool run_before_save_as_hook(snd_info *sp, const char *save_as_filename, bool selection, int srate,
1945 			     mus_sample_t smp_type, mus_header_t hd_type, const char *comment)
1946 {
1947   /* might be save-selection, as well as save-sound-as */
1948   if (before_save_as_hook_active) return(false);
1949   if (Xen_hook_has_list(before_save_as_hook))
1950     {
1951       Xen result;
1952       before_save_as_hook_active = true;
1953       result = run_progn_hook(before_save_as_hook,
1954 			      Xen_list_7((sp) ? C_int_to_Xen_sound(sp->index) : Xen_false,
1955 					 C_string_to_Xen_string(save_as_filename),
1956 					 C_bool_to_Xen_boolean(selection),
1957 					 C_int_to_Xen_integer(srate),
1958 					 C_int_to_Xen_integer((int)smp_type),
1959 					 C_int_to_Xen_integer((int)hd_type),
1960 					 (comment) ? C_string_to_Xen_string(comment) : Xen_false),
1961 			      S_before_save_as_hook);
1962       before_save_as_hook_active = false;
1963       return(Xen_is_true(result));
1964     }
1965   return(false);
1966 }
1967 
1968 
1969 /* -------- file dialog header/data choices -------- */
1970 
1971 enum {H_NEXT, H_AIFC, H_RIFF, H_RF64, H_RAW, H_AIFF, H_IRCAM, H_NIST, H_CAFF, /* the "built-in" choices for output */
1972       H_OGG, H_FLAC, H_SPEEX, H_WAVPACK,                                      /* readable/writable via external programs */
1973       H_MPEG, H_MIDI,                                                         /* readable via external programs */
1974       H_SIZE};
1975 
1976 static int h_num_sample_types[H_SIZE] = {12 /* next */, 13 /* aifc */,  8 /* riff */, 8 /* rf64 */, 18 /* raw */, 4 /* aiff */, 5  /* ircam */, 7 /* nist */, 13 /* caff */,
1977 				    1 /* ogg */,  1  /* flac */,  1 /* speex */, 1 /*wavpack */,
1978 				    1 /* mpeg */, 1  /* midi */};
1979 #define H_DFS_MAX 18
1980 
1981 static mus_sample_t h_dfs[H_SIZE][H_DFS_MAX] = { /* next */  {MUS_BFLOAT, MUS_BSHORT, MUS_LSHORT, MUS_LFLOAT,
1982 						     MUS_MULAW, MUS_BYTE, MUS_BINT, MUS_ALAW, MUS_B24INT, MUS_BDOUBLE, MUS_LINT, MUS_LDOUBLE},
1983 					/* aifc */  {MUS_BFLOAT, MUS_BSHORT, MUS_MULAW, MUS_BYTE, MUS_BINT, MUS_ALAW, MUS_B24INT,
1984 						     MUS_BDOUBLE, MUS_UBYTE, MUS_LSHORT, MUS_LINT, MUS_L24INT, MUS_UBSHORT},
1985 					/* riff */  {MUS_LFLOAT, MUS_LSHORT, MUS_MULAW, MUS_ALAW, MUS_UBYTE, MUS_LINT, MUS_LDOUBLE, MUS_L24INT},
1986 					/* rf64 */  {MUS_LFLOAT, MUS_LSHORT, MUS_MULAW, MUS_ALAW, MUS_UBYTE, MUS_LINT, MUS_LDOUBLE, MUS_L24INT},
1987 					/* raw  */  {MUS_BFLOAT, MUS_LFLOAT, MUS_BSHORT, MUS_MULAW, MUS_BYTE, MUS_BINT, MUS_ALAW,
1988 						     MUS_UBYTE, MUS_B24INT, MUS_BDOUBLE, MUS_LSHORT, MUS_LINT,
1989 						     MUS_LDOUBLE, MUS_UBSHORT, MUS_ULSHORT, MUS_L24INT, MUS_BINTN, MUS_LINTN},
1990 					/* aiff */  {MUS_BSHORT, MUS_BINT, MUS_BYTE, MUS_B24INT},
1991 					/* ircam */ {MUS_BFLOAT, MUS_BSHORT, MUS_MULAW, MUS_BINT, MUS_ALAW},
1992 					/* nist */  {MUS_BSHORT, MUS_LSHORT, MUS_BINT, MUS_LINT, MUS_BYTE, MUS_B24INT, MUS_L24INT},
1993 					/* caff  */ {MUS_BFLOAT, MUS_BSHORT, MUS_LFLOAT, MUS_LSHORT, MUS_MULAW, MUS_BYTE, MUS_BINTN, MUS_ALAW,
1994 						     MUS_B24INT, MUS_BDOUBLE, MUS_LINTN, MUS_L24INT, MUS_LDOUBLE},
1995 					/* ogg */   {MUS_LSHORT},
1996 					/* flac */  {MUS_LSHORT},
1997 					/* speex */ {MUS_LSHORT},
1998 					/* wavpack */ {MUS_LSHORT},
1999 					/* readonly */  {MUS_UNKNOWN_SAMPLE}, {MUS_UNKNOWN_SAMPLE}
2000 };
2001 static const char *h_df_names[H_SIZE][H_DFS_MAX];
2002 
2003 static const char *h_names[H_SIZE] = {"au/next  ", "aifc   ", "wave   ", "rf64  ", "raw    ", "aiff   ", "ircam ", "nist  ", "caff  ",
2004 				      "ogg   ", "flac  ", "speex ", "wavpack",
2005 				      "mpeg  ", "midi  "};
2006 static mus_header_t h_pos_to_type[H_SIZE] = {MUS_NEXT, MUS_AIFC, MUS_RIFF, MUS_RF64, MUS_RAW, MUS_AIFF, MUS_IRCAM, MUS_NIST, MUS_CAFF,
2007 					     MUS_UNKNOWN_HEADER, MUS_UNKNOWN_HEADER, MUS_UNKNOWN_HEADER, MUS_UNKNOWN_HEADER,
2008 					     MUS_UNKNOWN_HEADER, MUS_UNKNOWN_HEADER};
2009 static int h_type_to_pos[MUS_NUM_HEADERS];
2010 static int h_type_to_h[MUS_NUM_HEADERS];
2011 
2012 
sample_type_name(mus_sample_t sample_type)2013 static const char *sample_type_name(mus_sample_t sample_type)
2014 {
2015   switch (sample_type)
2016     {
2017     case MUS_BSHORT:           return("16-bit int (be)");
2018     case MUS_MULAW:            return("mulaw");
2019     case MUS_BYTE:             return("8-bit int");
2020     case MUS_BFLOAT:           return("float (be)");
2021     case MUS_BFLOAT_UNSCALED:  return("float unscaled (be))");
2022     case MUS_BINT:             return("32-bit int (be)");
2023     case MUS_ALAW:             return("alaw");
2024     case MUS_UBYTE:            return("unsigned byte");
2025     case MUS_B24INT:           return("24-bit int (be)");
2026     case MUS_BDOUBLE:          return("double (be)");
2027     case MUS_BDOUBLE_UNSCALED: return("double unscaled (be)");
2028     case MUS_LSHORT:           return("16-bit int (le)");
2029     case MUS_LINT:             return("32-bit int (le)");
2030     case MUS_LFLOAT:           return("float (le)");
2031     case MUS_LDOUBLE:          return("double (le)");
2032     case MUS_LFLOAT_UNSCALED:  return("float unscaled (le)");
2033     case MUS_LDOUBLE_UNSCALED: return("double unscaled (le)");
2034     case MUS_UBSHORT:          return("unsigned short (be)");
2035     case MUS_ULSHORT:          return("unsigned short (le)");
2036     case MUS_L24INT:           return("24-bit int (le)");
2037     case MUS_BINTN:            return("normalized int (be)");
2038     case MUS_LINTN:            return("normalized int (le)");
2039     default:                   return("unknown");
2040     }
2041 }
2042 
initialize_sample_type_lists(void)2043 void initialize_sample_type_lists(void)
2044 {
2045   int i, h;
2046 
2047   for (h = 0; h < H_SIZE; h++)
2048     for (i = 0; i < h_num_sample_types[h]; i++)
2049       h_df_names[h][i] = sample_type_name(h_dfs[h][i]);
2050 
2051   for (i = 0; i < MUS_NUM_HEADERS; i++)
2052     {
2053       h_type_to_pos[i] = -1;
2054       h_type_to_h[i] = -1;
2055     }
2056   h_type_to_pos[MUS_NEXT] = 0;
2057   h_type_to_pos[MUS_AIFC] = 1;
2058   h_type_to_pos[MUS_RIFF] = 2;
2059   h_type_to_pos[MUS_RF64] = 3;
2060   h_type_to_pos[MUS_RAW] = 4;
2061   h_type_to_pos[MUS_AIFF] = 5;
2062   h_type_to_pos[MUS_IRCAM] = 6;
2063   h_type_to_pos[MUS_NIST] = 7;
2064   h_type_to_pos[MUS_CAFF] = 8;
2065 
2066   h_type_to_h[MUS_NEXT] = H_NEXT;
2067   h_type_to_h[MUS_AIFC] = H_AIFC;
2068   h_type_to_h[MUS_RIFF] = H_RIFF;
2069   h_type_to_h[MUS_RF64] = H_RF64;
2070   h_type_to_h[MUS_RAW] = H_RAW;
2071   h_type_to_h[MUS_AIFF] = H_AIFF;
2072   h_type_to_h[MUS_IRCAM] = H_IRCAM;
2073   h_type_to_h[MUS_NIST] = H_NIST;
2074   h_type_to_h[MUS_CAFF] = H_CAFF;
2075   h_type_to_h[MUS_OGG] = H_OGG;
2076   h_type_to_h[MUS_FLAC] = H_FLAC;
2077   h_type_to_h[MUS_SPEEX] = H_SPEEX;
2078   h_type_to_h[MUS_MIDI] = H_MIDI;
2079   h_type_to_h[MUS_MPEG] = H_MPEG;
2080   h_type_to_h[MUS_WAVPACK] = H_WAVPACK;
2081 
2082   i = 9;
2083   /* readable/writable */
2084 #if HAVE_OGG
2085   h_type_to_pos[MUS_OGG] = i;
2086   h_pos_to_type[i++] = MUS_OGG;
2087 #endif
2088 
2089 #if HAVE_FLAC
2090   h_type_to_pos[MUS_FLAC] = i;
2091   h_pos_to_type[i++] = MUS_FLAC;
2092 #endif
2093 
2094 #if HAVE_SPEEX
2095   h_type_to_pos[MUS_SPEEX] = i;
2096   h_pos_to_type[i++] = MUS_SPEEX;
2097 #endif
2098 
2099 #if HAVE_WAVPACK
2100   h_type_to_pos[MUS_WAVPACK] = i;
2101   h_pos_to_type[i++] = MUS_WAVPACK;
2102 #endif
2103 
2104   /* readable */
2105 #if HAVE_MPEG
2106   h_type_to_pos[MUS_MPEG] = i;
2107   h_pos_to_type[i++] = MUS_MPEG;
2108 #endif
2109 
2110 #if HAVE_MIDI
2111   h_type_to_pos[MUS_MIDI] = i;
2112   h_pos_to_type[i++] = MUS_MIDI;
2113 #endif
2114 }
2115 
2116 
2117 #define NUM_BUILTIN_HEADERS 9
2118 #define NUM_POSSIBLE_HEADERS 16
2119 static const char **writable_headers = NULL;
2120 static const char **readable_headers = NULL;
2121 static int num_writable_headers = NUM_BUILTIN_HEADERS;
2122 static int num_readable_headers = NUM_BUILTIN_HEADERS;
2123 
2124 
short_builtin_headers(int * len)2125 const char **short_builtin_headers(int *len)
2126 {
2127   (*len) = NUM_BUILTIN_HEADERS;
2128   return(h_names);
2129 }
2130 
2131 
short_writable_headers(int * len)2132 const char **short_writable_headers(int *len)
2133 {
2134   /* these are headers that we either write ourself, or have external programs to write (oggenc etc) */
2135   if (!writable_headers)
2136     {
2137       int i;
2138       writable_headers = (const char **)calloc(NUM_POSSIBLE_HEADERS, sizeof(char *));
2139       for (i = 0; i < NUM_BUILTIN_HEADERS; i++)
2140 	writable_headers[i] = h_names[i];
2141 #if HAVE_OGG
2142       /* write temp as RIFF 16-bit lshort, then
2143 	 oggenc tempfile.wav -o outoggfile.ogg
2144 	 -q 6 sets to higher quality -- add as arg somehow?
2145 	 so format here is just the ogg output format: nothing settable
2146       */
2147       writable_headers[i++] = h_names[H_OGG];
2148 #endif
2149 
2150 #if HAVE_FLAC
2151       /* flac tempfile.wav -o output.flac */
2152       writable_headers[i++] = h_names[H_FLAC];
2153 #endif
2154 
2155 #if HAVE_SPEEX
2156       /* speexenc tempfile.wav output.spx */
2157       writable_headers[i++] = h_names[H_SPEEX];
2158 #endif
2159 
2160 #if HAVE_WAVPACK
2161       /* wavpack in -o out */
2162       writable_headers[i++] = h_names[H_WAVPACK];
2163 #endif
2164 
2165       num_writable_headers = i;
2166     }
2167   (*len) = num_writable_headers;
2168   return(writable_headers);
2169 }
2170 
2171 
short_readable_headers(int * len)2172 const char **short_readable_headers(int *len)
2173 {
2174   if (!readable_headers)
2175     {
2176       int i;
2177       readable_headers = (const char **)calloc(NUM_POSSIBLE_HEADERS, sizeof(char *));
2178       for (i = 0; i < NUM_BUILTIN_HEADERS; i++)
2179 	readable_headers[i] = h_names[i];
2180 #if HAVE_OGG
2181       /* oggdec to tempfile:
2182 	 oggdec infile.ogg -b 16 -o translatedfile.wav
2183 	 defaults for rest are signed little endian wav
2184 
2185 	 so sample_types (for output temp) here are not relevant -- no one will want 1 byte
2186       */
2187       readable_headers[i++] = h_names[H_OGG];
2188 #endif
2189 
2190 #if HAVE_FLAC
2191       /* flac -d infile.flac -o output.wav
2192        *  the -d -> decode
2193        *  no choices
2194        */
2195       readable_headers[i++] = h_names[H_FLAC];
2196 #endif
2197 
2198 #if HAVE_SPEEX
2199       /* speexdec infile.spx tempfile.wav
2200 	 no other choices
2201        */
2202       readable_headers[i++] = h_names[H_SPEEX];
2203 #endif
2204 
2205 #if HAVE_MPEG
2206       /* mpg321 -q -w output.wav input.mpg
2207 	   this is mpeg->wav only
2208 	   no choices
2209 	   mpg123 is probably the same
2210        */
2211       readable_headers[i++] = h_names[H_MPEG];
2212 #endif
2213 
2214 #if HAVE_TIMIDITY
2215       /* timidity input.mid -Ou -o output.snd
2216 	 midi->next/sun
2217 	 there are other choices...
2218       */
2219       readable_headers[i++] = h_names[H_MIDI];
2220 #endif
2221 
2222 #if HAVE_WAVPACK
2223       readable_headers[i++] = h_names[H_WAVPACK];
2224 #endif
2225       num_readable_headers = i;
2226     }
2227   (*len) = num_readable_headers;
2228   return(readable_headers);
2229 }
2230 
2231 
position_to_header_type(int position)2232 mus_header_t position_to_header_type(int position)
2233 {
2234   return(h_pos_to_type[position]);
2235 }
2236 
2237 
position_to_sample_type(mus_header_t header,int position)2238 mus_sample_t position_to_sample_type(mus_header_t header, int position)
2239 {
2240   return(h_dfs[h_type_to_pos[header]][position]);
2241 }
2242 
2243 
h_to_sample_type_pos(int h,mus_sample_t samp_type)2244 static int h_to_sample_type_pos(int h, mus_sample_t samp_type)
2245 {
2246   int i;
2247   for (i = 0; i < h_num_sample_types[h]; i++)
2248     if (h_dfs[h][i] == samp_type)
2249       return(i);
2250   return(0);
2251 }
2252 
2253 
header_type_and_sample_type_to_position(file_data * fdat,mus_header_t type,mus_sample_t sample_type)2254 const char **header_type_and_sample_type_to_position(file_data *fdat, mus_header_t type, mus_sample_t sample_type)
2255 {
2256   int h;
2257   h = h_type_to_h[type];
2258   fdat->sample_types = h_num_sample_types[h];
2259   fdat->header_type_pos = h_type_to_pos[type];
2260   fdat->sample_type_pos = h_to_sample_type_pos(h, sample_type);
2261   return(h_df_names[h]);
2262 }
2263 
2264 
position_to_header_type_and_sample_type(file_data * fdat,int pos)2265 void position_to_header_type_and_sample_type(file_data *fdat, int pos)
2266 {
2267   int h;
2268   h = h_type_to_h[h_pos_to_type[pos]];
2269   fdat->header_type_pos = pos;
2270   fdat->current_header_type = h_pos_to_type[pos];
2271   fdat->sample_type_pos = h_to_sample_type_pos(h, fdat->current_sample_type);
2272   fdat->current_sample_type = h_dfs[h][fdat->sample_type_pos];
2273 }
2274 
2275 
header_is_encoded(mus_header_t header_type)2276 bool header_is_encoded(mus_header_t header_type)
2277 {
2278   /* going either way here */
2279   return((header_type == MUS_OGG) ||
2280 	 (header_type == MUS_FLAC) ||
2281 	 (header_type == MUS_SPEEX) ||
2282 	 (header_type == MUS_MPEG) ||
2283 	 (header_type == MUS_MIDI) ||
2284 	 (header_type == MUS_WAVPACK)
2285 	 );
2286 }
2287 
2288 
quoted_filename(const char * filename,bool * new_name)2289 static char *quoted_filename(const char *filename, bool *new_name)
2290 {
2291   int p, len;
2292   p = strcspn(filename, "' *?"); /* also [] but do they actually ever happen? */
2293   len = strlen(filename);
2294   if (p < len)
2295     {
2296       int i, j;
2297       char *name;
2298       (*new_name) = true;
2299       name = (char *)calloc(len * 2, sizeof(char));
2300       for (i = 0, j = 0; i < len; i++)
2301 	{
2302 	  if ((filename[i] == ' ')  ||
2303 	      (filename[i] == '\'') ||
2304 	      (filename[i] == '*')  ||
2305 	      (filename[i] == '(')  ||
2306 	      (filename[i] == ')')  ||
2307 	      (filename[i] == '?'))
2308 	    name[j++] = '\\';
2309 	  name[j++] = filename[i];
2310 	}
2311       return(name);
2312     }
2313   (*new_name) = false;
2314   return((char *)filename);
2315 }
2316 
2317 
snd_encode(mus_header_t type,const char * input_filename,const char * output_filename)2318 void snd_encode(mus_header_t type, const char *input_filename, const char *output_filename)
2319 {
2320   /* write lshort wav tmpfile, encode, remove tmpfile */
2321   char *command = NULL, *ofile, *ifile;
2322   bool ofile_free = false, ifile_free = false;
2323 
2324   if (mus_file_probe(output_filename))
2325     snd_remove(output_filename, IGNORE_CACHE);
2326   /* these guys balk at overwriting */
2327 
2328   /* can't use '%s' here because the filename might also have single-quotes!
2329    */
2330   ifile = quoted_filename(input_filename, &ifile_free);
2331   ofile = quoted_filename(output_filename, &ofile_free);
2332 
2333   switch (type)
2334     {
2335 #if HAVE_OGG
2336     case MUS_OGG:
2337       command = mus_format("%s %s -o %s", PATH_OGGENC, ifile, ofile);
2338       break;
2339 #endif
2340 
2341 #if HAVE_FLAC
2342     case MUS_FLAC:
2343       command = mus_format("%s %s -o %s", PATH_FLAC, ifile, ofile);
2344       break;
2345 #endif
2346 
2347 #if HAVE_SPEEX
2348     case MUS_SPEEX:
2349       command = mus_format("%s %s %s", PATH_SPEEXENC, ifile, ofile);
2350       break;
2351 #endif
2352 
2353 #if HAVE_WAVPACK
2354     case MUS_WAVPACK:
2355       command = mus_format("%s %s -o %s", PATH_WAVPACK, ifile, ofile);
2356       break;
2357 #endif
2358 
2359     default:
2360       break;
2361     }
2362 
2363   if (ofile_free) free(ofile);
2364   if (ifile_free) free(ifile);
2365 
2366   if (command)
2367     {
2368       int err;
2369       err = system(command);
2370       free(command);
2371       if (err == -1)
2372 	fprintf(stderr, "%s failed?", command);
2373     }
2374 }
2375 
2376 
snd_decode(mus_header_t type,const char * input_filename,const char * output_filename)2377 int snd_decode(mus_header_t type, const char *input_filename, const char *output_filename)
2378 {
2379   int err = 0;
2380   char *command = NULL, *ofile, *ifile;
2381   bool ofile_free = false, ifile_free = false;
2382 
2383   if (mus_file_probe(output_filename))
2384     snd_remove(output_filename, IGNORE_CACHE);
2385   /* these guys balk at overwriting */
2386 
2387   ifile = quoted_filename(input_filename, &ifile_free);
2388   ofile = quoted_filename(output_filename, &ofile_free);
2389 
2390   switch (type)
2391     {
2392 #if HAVE_OGG
2393     case MUS_OGG:
2394       command = mus_format("%s %s -b 16 -o %s", PATH_OGGDEC, ifile, ofile);
2395       break;
2396 #endif
2397 
2398 #if HAVE_FLAC
2399     case MUS_FLAC:
2400       command = mus_format("%s -d %s -o %s", PATH_FLAC, ifile, ofile);
2401       break;
2402 #endif
2403 
2404 #if HAVE_SPEEX
2405     case MUS_SPEEX:
2406       command = mus_format("%s %s %s", PATH_SPEEXDEC, ifile, ofile);
2407       break;
2408 #endif
2409 
2410 #if HAVE_MPEG
2411     case MUS_MPEG:
2412 #if HAVE_MPG321
2413       command = mus_format("%s -q -w %s %s", PATH_MPG321, ofile, ifile);
2414 #else
2415       command = mus_format("%s -q -w %s %s", PATH_MPG123, ofile, ifile);
2416 #endif
2417       break;
2418 #endif
2419 
2420 #if HAVE_TIMIDITY
2421     case MUS_MIDI:
2422       command = mus_format("%s %s -Ou -o %s", PATH_TIMIDITY, ifile, ofile);
2423       break;
2424 #endif
2425 
2426 #if HAVE_WAVPACK
2427     case MUS_WAVPACK:
2428       command = mus_format("%s %s -o %s", PATH_WVUNPACK, ifile, ofile);
2429       break;
2430 #endif
2431     default:
2432       err = -1;
2433       break;
2434     }
2435 
2436   if (ofile_free) free(ofile);
2437   if (ifile_free) free(ifile);
2438 
2439   if (command)
2440     {
2441       int err;
2442       err = system(command);
2443       free(command);
2444       if (err == -1)
2445 	fprintf(stderr, "%s failed?", command);
2446     }
2447   return(err);
2448 }
2449 
2450 
2451 typedef struct {
2452   snd_info *parlous_sp, *current_sp;
2453   char *filename;
2454 } same_name_info;
2455 
check_for_same_name(snd_info * sp1,same_name_info * info)2456 static bool check_for_same_name(snd_info *sp1, same_name_info *info)
2457 {
2458   if ((sp1) &&
2459       (sp1 != info->current_sp) &&
2460       (mus_strcmp(sp1->filename, info->filename)))
2461     {
2462       if (has_unsaved_edits(sp1))
2463 	{
2464 	  info->parlous_sp = sp1;
2465 	  return(true);
2466 	}
2467     }
2468   return(false);
2469 }
2470 
2471 
map_over_sounds_with_collision(bool (* func)(snd_info *,same_name_info * col),same_name_info * collision)2472 static void map_over_sounds_with_collision(bool (*func)(snd_info *, same_name_info *col), same_name_info *collision)
2473 {
2474   /* true = abort map, skips inactive sounds */
2475   int i;
2476   for (i = 0; i < ss->max_sounds; i++)
2477     {
2478       snd_info *sp;
2479       sp = ss->sounds[i];
2480       if ((sp) && (sp->inuse == SOUND_NORMAL))
2481 	{
2482 	  bool val;
2483 	  val = (*func)(sp, collision);
2484 	  if (val) return;
2485 	}
2486     }
2487 }
2488 
2489 
file_is_open_elsewhere_and_has_unsaved_edits(snd_info * sp,const char * fullname)2490 snd_info *file_is_open_elsewhere_and_has_unsaved_edits(snd_info *sp, const char *fullname)
2491 {
2492   same_name_info *collision;
2493   snd_info *result;
2494   collision = (same_name_info *)calloc(1, sizeof(same_name_info));
2495   collision->filename = (char *)fullname;
2496   collision->parlous_sp = NULL;
2497   collision->current_sp = sp;
2498   map_over_sounds_with_collision(check_for_same_name, collision);
2499   result = collision->parlous_sp;
2500   free(collision);
2501   return(result);
2502 }
2503 
2504 
edit_header_callback(snd_info * sp,file_data * edit_header_data,void (* outer_handler)(const char * error_msg,void * ufd),void (* inner_handler)(const char * error_msg,void * ufd))2505 bool edit_header_callback(snd_info *sp, file_data *edit_header_data,
2506 			  void (*outer_handler)(const char *error_msg, void *ufd),
2507 			  void (*inner_handler)(const char *error_msg, void *ufd))
2508 {
2509   /* this just changes the header -- it does not actually reformat the data or whatever */
2510 
2511   mus_long_t loc, samples;
2512   char *comment, *original_comment = NULL;
2513   file_info *hdr;
2514   int chans, srate;
2515   mus_sample_t sample_type;
2516   mus_header_t header_type;
2517 
2518   if ((sp->user_read_only == FILE_READ_ONLY) ||
2519       (sp->file_read_only == FILE_READ_ONLY))
2520     {
2521       snd_error("%s is write-protected", sp->filename);
2522       return(false);
2523     }
2524 #ifndef _MSC_VER
2525   if (!(ss->file_monitor_ok))
2526     if (access(sp->filename, W_OK) < 0)
2527       {
2528 	snd_error("%s is write-protected", sp->filename);
2529 	return(false);
2530       }
2531 #endif
2532   hdr = sp->hdr;
2533 
2534   /* find out which fields changed -- if possible don't touch the sound data */
2535   redirect_snd_error_to(inner_handler, (void *)edit_header_data);
2536   comment = get_file_dialog_sound_attributes(edit_header_data, &srate, &chans, &header_type, &sample_type, &loc, &samples, 1);
2537   redirect_snd_error_to(outer_handler, (void *)edit_header_data);
2538   if (edit_header_data->error_widget != NOT_A_SCANF_WIDGET) /* bad field value, perhaps */
2539     return(false);
2540 
2541   if (sp->hdr->type != MUS_RAW)
2542     {
2543       original_comment = mus_sound_comment(sp->filename);
2544       if ((hdr->type == MUS_AIFF) ||
2545 	  (hdr->type == MUS_AIFC))
2546 	mus_header_set_aiff_loop_info(mus_sound_loop_info(sp->filename));
2547     }
2548   mus_sound_forget(sp->filename);
2549   if (hdr->type != header_type)
2550     {
2551       sp->writing = true;
2552       mus_header_change_type(sp->filename, header_type, sample_type);
2553       sp->writing = false;
2554     }
2555   else
2556     {
2557       if (hdr->sample_type != sample_type)
2558 	mus_header_change_sample_type(sp->filename, header_type, sample_type);
2559     }
2560   if (hdr->chans != chans)
2561     mus_header_change_chans(sp->filename, header_type, chans);
2562   if (hdr->srate != srate)
2563     mus_header_change_srate(sp->filename, header_type, srate);
2564   if (hdr->samples != samples)
2565     mus_header_change_data_size(sp->filename, header_type, mus_samples_to_bytes(sample_type, samples));
2566   if ((header_type == MUS_NEXT) &&
2567       (hdr->data_location != loc))
2568     mus_header_change_location(sp->filename, MUS_NEXT, loc);
2569   if (!(mus_strcmp(comment, original_comment)))
2570     mus_header_change_comment(sp->filename, header_type, comment);
2571   if (comment) free(comment);
2572   if (original_comment) free(original_comment);
2573   snd_update(sp);
2574   return(true);
2575 }
2576 
2577 
2578 #if (!USE_NO_GUI)
2579 /* raw data dialog funcs */
2580 
swap_int(int n)2581 static int swap_int(int n)
2582 {
2583   int o;
2584   unsigned char *inp, *outp;
2585   inp = (unsigned char *)&n;
2586   outp = (unsigned char *)&o;
2587   outp[0] = inp[3];
2588   outp[1] = inp[2];
2589   outp[2] = inp[1];
2590   outp[3] = inp[0];
2591   return(o);
2592 }
2593 
2594 
swap_mus_long_t(mus_long_t n)2595 static mus_long_t swap_mus_long_t(mus_long_t n)
2596 {
2597   mus_long_t o;
2598   unsigned char *inp, *outp;
2599   inp = (unsigned char *)&n;
2600   outp = (unsigned char *)&o;
2601   outp[0] = inp[7];
2602   outp[1] = inp[6];
2603   outp[2] = inp[5];
2604   outp[3] = inp[4];
2605   outp[4] = inp[3];
2606   outp[5] = inp[2];
2607   outp[6] = inp[1];
2608   outp[7] = inp[0];
2609   return(o);
2610 }
2611 
2612 
swap_short(short n)2613 static short swap_short(short n)
2614 {
2615   short o;
2616   unsigned char *inp, *outp;
2617   inp = (unsigned char *)&n;
2618   outp = (unsigned char *)&o;
2619   outp[0] = inp[1];
2620   outp[1] = inp[0];
2621   return(o);
2622 }
2623 
2624 
raw_data_explanation(const char * filename,file_info * hdr,char ** info)2625 static char *raw_data_explanation(const char *filename, file_info *hdr, char **info)
2626 {
2627   char *reason_str, *tmp_str, *file_string;
2628   mus_long_t nsamp;
2629   bool ok;
2630   int ns, better_srate = 0, better_chans = 0, len;
2631   reason_str = (char *)calloc(PRINT_BUFFER_SIZE, sizeof(char));
2632   tmp_str = (char *)calloc(LABEL_BUFFER_SIZE, sizeof(char));
2633   /* try to provide some notion of what might be the intended header (currently limited to byte-order mistakes) */
2634   len = PRINT_BUFFER_SIZE;
2635 
2636   /* srate */
2637   ok = ((original_srate >= 8000) &&
2638 	(original_srate <= 100000));
2639   snprintf(reason_str, len, "srate%s: %d", (ok) ? "" : " looks wrong", original_srate);
2640   if (!ok)
2641     {
2642       ns = (int)swap_int(original_srate);
2643       if ((ns < 4000) || (ns > 100000))
2644 	ns = (int)swap_short((short)(original_srate));
2645       if ((ns > 4000) && (ns < 100000))
2646 	{
2647 	  better_srate = ns;
2648 	  snprintf(tmp_str, LABEL_BUFFER_SIZE, " (swapped: %d)", ns);
2649 	  reason_str = mus_strcat(reason_str, tmp_str, &len);
2650 	}
2651     }
2652 
2653   /* chans */
2654   ok = ((original_chans > 0) &&
2655 	(original_chans < 1000));
2656   snprintf(tmp_str, LABEL_BUFFER_SIZE, "\nchans%s: %d", (ok) ? "" : " looks wrong", original_chans);
2657   reason_str = mus_strcat(reason_str, tmp_str, &len);
2658   if (!ok)
2659     {
2660       ns = swap_int(original_chans);
2661       if ((ns < 0) || (ns > 8))
2662 	ns = swap_short((short)(original_chans));
2663       if ((ns > 0) && (ns <= 8))
2664 	{
2665 	  better_chans = ns;
2666 	  snprintf(tmp_str, LABEL_BUFFER_SIZE, " (swapped: %d)", ns);
2667 	  reason_str = mus_strcat(reason_str, tmp_str, &len);
2668 	}
2669     }
2670 
2671   /* header type */
2672   snprintf(tmp_str, LABEL_BUFFER_SIZE, "\ntype: %s", mus_header_type_name(hdr->type));
2673   reason_str = mus_strcat(reason_str, tmp_str, &len);
2674 
2675   /* sample type */
2676   if (!(mus_is_sample_type(original_sample_type)))
2677     {
2678       char *format_info;
2679       if (original_sample_type != MUS_UNKNOWN_SAMPLE)
2680 	format_info = (char *)mus_sample_type_name(original_sample_type);
2681       else format_info = (char *)mus_header_original_sample_type_name(mus_sound_original_sample_type(filename), hdr->type);
2682       snprintf(tmp_str, LABEL_BUFFER_SIZE, "\nformat looks bogus: %s", format_info);
2683     }
2684   else snprintf(tmp_str, LABEL_BUFFER_SIZE, "\nformat: %s", (char *)mus_sample_type_name(original_sample_type));
2685   reason_str = mus_strcat(reason_str, tmp_str, &len);
2686 
2687   /* samples */
2688   snprintf(tmp_str, LABEL_BUFFER_SIZE, "\nlength: %.3f (%" print_mus_long " samples, %" print_mus_long " bytes total)",
2689 	       ((double)(hdr->samples) / (double)(hdr->chans * hdr->srate)),
2690 	       hdr->samples,
2691 	       mus_sound_length(filename));
2692   reason_str = mus_strcat(reason_str, tmp_str, &len);
2693   nsamp = swap_mus_long_t(hdr->samples);
2694   if (nsamp < mus_sound_length(filename))
2695     {
2696       snprintf(tmp_str, LABEL_BUFFER_SIZE, " (swapped: %" print_mus_long , nsamp);
2697       reason_str = mus_strcat(reason_str, tmp_str, &len);
2698       if ((better_chans) && (better_srate))
2699 	{
2700 	  snprintf(tmp_str, LABEL_BUFFER_SIZE,
2701 		       ", swapped length: %.3f / sample-size-in-bytes)",
2702 		       ((double)nsamp / (double)(better_chans * better_srate)));
2703 	  reason_str = mus_strcat(reason_str, tmp_str, &len);
2704 	}
2705       else reason_str = mus_strcat(reason_str, ")", &len);
2706     }
2707 
2708   /* data location */
2709   snprintf(tmp_str, LABEL_BUFFER_SIZE, "\ndata location: %" print_mus_long, hdr->data_location);
2710   reason_str = mus_strcat(reason_str, tmp_str, &len);
2711   nsamp = swap_mus_long_t(hdr->data_location);
2712   if ((nsamp > 0) &&
2713       (nsamp <= 1024))
2714     {
2715       snprintf(tmp_str, LABEL_BUFFER_SIZE, " (swapped: %" print_mus_long ")", nsamp);
2716       reason_str = mus_strcat(reason_str, tmp_str, &len);
2717     }
2718   (*info) = reason_str;
2719   file_string = (char *)calloc(PRINT_BUFFER_SIZE, sizeof(char));
2720   snprintf(file_string, PRINT_BUFFER_SIZE,
2721 	       "Bad header found on %s",
2722 	       filename_without_directory(filename));
2723   free(tmp_str);
2724   free_file_info(hdr);
2725   return(file_string);
2726 }
2727 #endif
2728 
2729 
dialog_get_title(widget_t dialog)2730 char *dialog_get_title(widget_t dialog)
2731 {
2732 #if USE_MOTIF
2733   char *temp, *titlestr = NULL;
2734   XmString title;
2735   XtVaGetValues(dialog, XmNdialogTitle, &title, NULL);
2736   temp = (char *)XmStringUnparse(title, NULL, XmCHARSET_TEXT, XmCHARSET_TEXT, NULL, 0, XmOUTPUT_ALL);
2737   if (temp)
2738     {
2739       titlestr = mus_strdup(temp);
2740       XtFree(temp);
2741     }
2742   return(titlestr);
2743 #endif
2744 #if USE_NO_GUI
2745   return(NULL); /* make the compiler happy */
2746 #endif
2747 }
2748 
2749 
2750 
2751 /* -------- info popup -------- */
2752 
2753 #if (!USE_NO_GUI)
2754 
display_file_maxamps(const char * filename,int chans)2755 static char *display_file_maxamps(const char *filename, int chans)
2756 {
2757   char *ampstr = NULL;
2758   int i, len;
2759   mus_float_t *vals;
2760   mus_long_t *times;
2761   len = chans * 32;
2762   ampstr = (char *)calloc(len, sizeof(char));
2763   vals = (mus_float_t *)calloc(chans, sizeof(mus_float_t));
2764   times = (mus_long_t *)calloc(chans, sizeof(mus_long_t));
2765   snprintf(ampstr, len, "maxamp%s: ", (chans > 1) ? "s" : "");
2766   mus_sound_maxamps(filename, chans, vals, times);
2767   for (i = 0; i < chans; i++)
2768     {
2769       ampstr = mus_strcat(ampstr, prettyf(vals[i], 3), &len);
2770       ampstr = mus_strcat(ampstr, " ", &len);
2771     }
2772   free(vals);
2773   free(times);
2774   return(ampstr);
2775 }
2776 
2777 
display_sound_maxamps(snd_info * sp)2778 static char *display_sound_maxamps(snd_info *sp)
2779 {
2780   char *ampstr = NULL;
2781   uint32_t i;
2782   int len;
2783   len = sp->nchans * 32;
2784   ampstr = (char *)calloc(len, sizeof(char));
2785   snprintf(ampstr, len, "maxamp%s: ", (sp->nchans > 1) ? "s" : "");
2786   for (i = 0; i < sp->nchans; i++)
2787     {
2788       ampstr = mus_strcat(ampstr, prettyf(channel_maxamp(sp->chans[i], AT_CURRENT_EDIT_POSITION), 3), &len);
2789       ampstr = mus_strcat(ampstr, " ", &len);
2790     }
2791   return(ampstr);
2792 }
2793 
2794 
display_info(snd_info * sp)2795 void display_info(snd_info *sp)
2796 {
2797   #define INFO_BUFFER_SIZE 1024
2798   if (sp)
2799     {
2800       file_info *hdr;
2801       char *comment = NULL, *ampstr = NULL, *buffer = NULL;
2802 
2803       hdr = sp->hdr;
2804       buffer = (char *)calloc(INFO_BUFFER_SIZE, sizeof(char));
2805 
2806       snprintf(buffer, INFO_BUFFER_SIZE,
2807 		   "srate: %d\nchans: %d\nlength: %.3f (%" print_mus_long " %s)\n%s\n",
2808 		   snd_srate(sp),
2809 		   sp->nchans,
2810 		   (double)(current_samples(sp->chans[0])) / (double)snd_srate(sp),
2811 		   current_samples(sp->chans[0]),
2812 		   (sp->nchans == 1) ? "samples" : "framples",
2813 		   ampstr = display_sound_maxamps(sp));
2814       post_it(sp->short_filename, buffer);
2815       if (ampstr) free(ampstr);
2816 
2817       snprintf(buffer, INFO_BUFFER_SIZE, "\n----------------------------------------\n%s:", sp->filename);
2818       post_it_append(buffer);
2819 
2820       snprintf(buffer, INFO_BUFFER_SIZE, "\n    type: %s\n    format: %s\n    written: %s\n",
2821 		   mus_header_type_name(hdr->type),
2822 		   mus_sample_type_name(hdr->sample_type),
2823 		   snd_strftime(STRFTIME_FORMAT, sp->write_date));
2824       post_it_append(buffer);
2825 
2826       if (hdr->srate != snd_srate(sp))
2827 	{
2828 	  snprintf(buffer, INFO_BUFFER_SIZE, "    original srate: %d\n", hdr->srate);
2829 	  post_it_append(buffer);
2830 	}
2831 
2832       if (hdr->chans != (int)sp->nchans)
2833 	{
2834 	  snprintf(buffer, INFO_BUFFER_SIZE, "    original chans: %d\n", hdr->chans);
2835 	  post_it_append(buffer);
2836 	}
2837 
2838       comment = hdr->comment;
2839       while ((comment) && (*comment) &&
2840 	     (((*comment) == '\n') ||
2841 	      ((*comment) == '\t') ||
2842 	      ((*comment) == ' ') ||
2843 	      ((*comment) == '\xd')))
2844 	comment++;
2845       if ((comment) && (*comment))
2846 	{
2847 	  snprintf(buffer, INFO_BUFFER_SIZE, "    comment: \"%s\"\n", comment);
2848 	  post_it_append(buffer);
2849 	}
2850 
2851       if (mus_sound_maxamp_exists(sp->filename))
2852 	{
2853 	  uint32_t i;
2854 	  bool edits = false;
2855 	  for (i = 0; i < sp->nchans; i++)
2856 	    if (sp->chans[i]->edit_ctr > 0)
2857 	      {
2858 		edits = true;
2859 		break;
2860 	      }
2861 
2862 	  if (edits)
2863 	    {
2864 	      ampstr = display_file_maxamps(sp->filename, sp->nchans);
2865 	      if (ampstr)
2866 		{
2867 		  snprintf(buffer, INFO_BUFFER_SIZE, "    original %s\n", ampstr);
2868 		  post_it_append(buffer);
2869 		  free(ampstr);
2870 		}
2871 	    }
2872 	}
2873       free(buffer);
2874     }
2875 }
2876 #endif
2877 
2878 
2879 
2880 /* -------- extlang connections -------- */
2881 
2882 
g_add_sound_file_extension(Xen ext)2883 static Xen g_add_sound_file_extension(Xen ext)
2884 {
2885   #define H_add_sound_file_extension "(" S_add_sound_file_extension " ext):  add the file extension 'ext' to the list of sound file extensions"
2886   Xen_check_type(Xen_is_string(ext), ext, 1, S_add_sound_file_extension, "a string");
2887   add_sound_file_extension(Xen_string_to_C_string(ext));
2888   return(ext);
2889 }
2890 
2891 
g_sound_file_extensions(void)2892 static Xen g_sound_file_extensions(void)
2893 {
2894   #define H_sound_file_extensions "(" S_sound_file_extensions "): list of current sound file extensions (used \
2895 by the just-sounds file filters)"
2896 
2897   Xen res = Xen_empty_list;
2898   int i;
2899   for (i = 0; i < sound_file_extensions_end; i++)
2900     res = Xen_cons(C_string_to_Xen_string(sound_file_extensions[i]),
2901 		   res);
2902   return(res);
2903 }
2904 
2905 
g_set_sound_file_extensions(Xen lst)2906 static Xen g_set_sound_file_extensions(Xen lst)
2907 {
2908   int i, len;
2909   for (i = 0; i < sound_file_extensions_end; i++)
2910     if (sound_file_extensions[i])
2911       {
2912 	free(sound_file_extensions[i]);
2913 	sound_file_extensions[i] = NULL;
2914       }
2915   sound_file_extensions_end = 0;
2916   default_sound_file_extensions = 0;
2917   len = Xen_list_length(lst);
2918   for (i = 0; i < len; i++)
2919     if (!(Xen_is_string(Xen_list_ref(lst, i))))
2920       {
2921 	Xen_check_type(false, Xen_list_ref(lst, i), i, S_set S_sound_file_extensions, "a filename extension (a string like \"snd\")");
2922 	return(Xen_false);
2923       }
2924   for (i = 0; i < len; i++)
2925     add_sound_file_extension(Xen_string_to_C_string(Xen_list_ref(lst, i)));
2926   return(lst);
2927 }
2928 
2929 
g_file_write_date(Xen file)2930 static Xen g_file_write_date(Xen file)
2931 {
2932   #if HAVE_RUBY
2933     #define write_date_equivalent "Equivalent to Ruby's File.mtime(file)"
2934   #endif
2935 
2936   #if HAVE_FORTH
2937     #define write_date_equivalent "Equivalent to Forth's file-mtime"
2938   #endif
2939 
2940   #if HAVE_SCHEME
2941     #define write_date_equivalent ""
2942   #endif
2943 
2944   #define S_file_write_date "file-write-date"
2945 #ifndef __GNUC__
2946   #define H_file_write_date "(" S_file_write_date " file): write date of file"
2947 #else
2948   #define H_file_write_date "(" S_file_write_date " file): write date in the same format as \
2949 current-time:\n(strftime \"%a %d-%b-%Y %H:%M %Z\" (localtime (" S_file_write_date " \"oboe.snd\")))\n" write_date_equivalent
2950 #endif
2951 
2952   time_t date;
2953   Xen_check_type(Xen_is_string(file), file, 1, S_file_write_date, "a string");
2954   date = file_write_date(Xen_string_to_C_string(file));
2955   return(C_int_to_Xen_integer(date));
2956 }
2957 
2958 
g_sound_loop_info(Xen snd)2959 static Xen g_sound_loop_info(Xen snd)
2960 {
2961   #define H_sound_loop_info "(" S_sound_loop_info " :optional snd): return the sound's loop points as a \
2962 list: (sustain-start sustain-end release-start release-end baseNote detune)"
2963   int *res;
2964   snd_info *sp;
2965   Snd_assert_sound(S_sound_loop_info, snd, 1);
2966   sp = get_sp(snd);
2967   if (!sp)
2968     return(snd_no_such_sound_error(S_sound_loop_info, snd));
2969   res = sp->hdr->loops;
2970   if (res)
2971     return(Xen_list_8(C_int_to_Xen_integer(res[0]), C_int_to_Xen_integer(res[1]), C_int_to_Xen_integer(res[2]),
2972 		      C_int_to_Xen_integer(res[3]), C_int_to_Xen_integer(res[4]), C_int_to_Xen_integer(res[5]),
2973 		      C_int_to_Xen_integer(res[6]), C_int_to_Xen_integer(res[7])));
2974   return(Xen_empty_list);
2975 }
2976 
2977 
g_set_sound_loop_info(Xen snd,Xen vals)2978 static Xen g_set_sound_loop_info(Xen snd, Xen vals)
2979 {
2980   snd_info *sp;
2981   char *tmp_file;
2982   file_info *hdr;
2983   mus_header_t type;
2984   int len = 0;
2985 
2986   Xen start0 = Xen_integer_zero, end0 = Xen_integer_zero;
2987   Xen start1 = Xen_integer_zero, end1 = Xen_integer_zero;
2988   Xen mode0 = Xen_integer_zero, mode1 = Xen_integer_zero;
2989   Xen note = Xen_integer_zero, detune = Xen_integer_zero;
2990   Xen_check_type((!Xen_is_bound(vals)) || (Xen_is_list(vals)), vals, 2, S_set S_sound_loop_info, "a list");
2991 
2992   if (!Xen_is_bound(vals))
2993     {
2994       /* what is going on here? -- (set! (sound-loop-info) (list...))? */
2995       Xen_check_type(Xen_is_list(snd), snd, 1, S_set S_sound_loop_info, "a list");
2996       vals = snd;
2997       sp = get_sp(Xen_undefined);
2998     }
2999   else
3000     {
3001       Snd_assert_sound(S_set S_sound_loop_info, snd, 1);
3002       sp = get_sp(snd);
3003     }
3004   len = Xen_list_length(vals);
3005 
3006   if (!sp)
3007     return(snd_no_such_sound_error(S_set S_sound_loop_info, snd));
3008 
3009   if ((sp->user_read_only == FILE_READ_ONLY) ||
3010       (sp->file_read_only == FILE_READ_ONLY))
3011     Xen_error(Xen_make_error_type("cannot-save"),
3012 	      Xen_list_2(C_string_to_Xen_string(S_set S_sound_loop_info ": ~S is write-protected"),
3013 			 C_string_to_Xen_string(sp->filename)));
3014 
3015   hdr = sp->hdr;
3016   if (len > 0)
3017     {
3018       start0 = Xen_list_ref(vals, 0);
3019       Xen_check_type(Xen_is_integer(start0), start0, 2, S_set S_sound_loop_info, "start0 must be an integer");
3020       if (len > 1)
3021 	{
3022 	  end0 = Xen_list_ref(vals, 1);
3023 	  Xen_check_type(Xen_is_integer(end0), end0, 2, S_set S_sound_loop_info, "end0 must be an integer");
3024 	  if (len > 2)
3025 	    {
3026 	      start1 = Xen_list_ref(vals, 2);
3027 	      Xen_check_type(Xen_is_integer(start1), start1, 2, S_set S_sound_loop_info, "start1 must be an integer");
3028 	      if (len > 3)
3029 		{
3030 		  end1 = Xen_list_ref(vals, 3);
3031 		  Xen_check_type(Xen_is_integer(end1), end1, 2, S_set S_sound_loop_info, "end1 must be an integer");
3032 		  if (len > 4)
3033 		    {
3034 		      note = Xen_list_ref(vals, 4);
3035 		      Xen_check_type(Xen_is_integer(note), note, 2, S_set S_sound_loop_info, "note must be an integer");
3036 		      if (len > 5)
3037 			{
3038 			  detune = Xen_list_ref(vals, 5);
3039 			  Xen_check_type(Xen_is_integer(detune), detune, 2, S_set S_sound_loop_info, "detune must be an integer");
3040 			  if (len > 6)
3041 			    {
3042 			      mode0 = Xen_list_ref(vals, 6);
3043 			      Xen_check_type(Xen_is_integer(mode0), mode0, 2, S_set S_sound_loop_info, "mode0 must be an integer");
3044 			      if (len > 7)
3045 				{
3046 				mode1 = Xen_list_ref(vals, 7);
3047 				Xen_check_type(Xen_is_integer(mode1), mode1, 2, S_set S_sound_loop_info, "mode1 must be an integer");
3048 				}}}}}}}}
3049 
3050   if (!hdr->loops)
3051     hdr->loops = (int *)calloc(MUS_LOOP_INFO_SIZE, sizeof(int));
3052   else memset((void *)(hdr->loops), 0, MUS_LOOP_INFO_SIZE * sizeof(int));
3053   hdr->loops[0] = Xen_integer_to_C_int(start0);
3054   hdr->loops[1] = Xen_integer_to_C_int(end0);
3055   hdr->loops[2] = Xen_integer_to_C_int(start1);
3056   hdr->loops[3] = Xen_integer_to_C_int(end1);
3057   if (len > 4)
3058     {
3059       hdr->loops[4] = Xen_integer_to_C_int(note);
3060       hdr->loops[5] = Xen_integer_to_C_int(detune);
3061     }
3062   if (len > 6)
3063     {
3064       hdr->loops[6] = Xen_integer_to_C_int(mode0);
3065       hdr->loops[7] = Xen_integer_to_C_int(mode1);
3066     }
3067   else
3068     {
3069       if (!(Xen_is_false(end0))) hdr->loops[6] = 1;
3070       if (!(Xen_is_false(end1))) hdr->loops[7] = 1;
3071     }
3072   mus_sound_set_loop_info(sp->filename, hdr->loops);
3073   mus_header_set_aiff_loop_info(hdr->loops);
3074 
3075   type = hdr->type;
3076   if ((type != MUS_AIFF) &&
3077       (type != MUS_AIFC))
3078     {
3079       snd_warning("changing %s's header from %s to aifc to accommodate loop info",
3080 		  sp->short_filename,
3081 		  mus_header_type_name(type));
3082       type = MUS_AIFC;
3083     }
3084   /* ideally set sound_loop_info would just change the header (keeping all other state intact)
3085    *   but this aspect of AIFC headers is impossibly complicated (if we could assume our own headers,
3086    *   it would not be so hard).
3087    */
3088   tmp_file = snd_tempnam();
3089   {
3090     io_error_t err;
3091     err = save_edits_without_display(sp, tmp_file, type,
3092 				     hdr->sample_type,
3093 				     hdr->srate,
3094 				     hdr->comment,
3095 				     AT_CURRENT_EDIT_POSITION);
3096     if ((err != IO_NO_ERROR) &&
3097 	(err != IO_SAVE_HOOK_CANCELLATION))
3098       {
3099 	Xen_error(Xen_make_error_type("cannot-save"),
3100 		  Xen_list_3(C_string_to_Xen_string(S_set S_sound_loop_info ": can't save ~S, ~A"),
3101 			     C_string_to_Xen_string(tmp_file),
3102 			     C_string_to_Xen_string(snd_io_strerror())));
3103 	return(Xen_false); /* not executed -- just for emphasis */
3104       }
3105     sp->writing = true;
3106     if (err == IO_SAVE_HOOK_CANCELLATION)
3107       snd_remove(tmp_file, IGNORE_CACHE);
3108     else
3109       {
3110 	err = move_file(tmp_file, sp->filename);
3111 	if (is_serious_io_error(err))
3112 	  {
3113 	    free(tmp_file);
3114 	    sp->writing = false;
3115 	    Xen_error(Xen_make_error_type("cant-update-file"),
3116 		      Xen_list_4(C_string_to_Xen_string(S_set S_sound_loop_info ": can't update ~S: ~A ~A"),
3117 				 C_string_to_Xen_string(sp->filename),
3118 				 C_string_to_Xen_string(io_error_name(err)),
3119 				 C_string_to_Xen_string(snd_io_strerror())));
3120 	    return(Xen_false);
3121 	  }
3122       }
3123     sp->writing = false;
3124     if (err != IO_SAVE_HOOK_CANCELLATION)
3125       snd_update(sp);
3126     free(tmp_file);
3127     return((err == IO_NO_ERROR) ? Xen_true : C_int_to_Xen_integer((int)err));
3128   }
3129 }
3130 
3131 
g_soundfont_info(Xen snd)3132 static Xen g_soundfont_info(Xen snd)
3133 {
3134   /* return all soundfont descriptors as list of lists: ((name start loopstart loopend)) */
3135   #define H_soundfont_info "(" S_soundfont_info " :optional snd): list of lists describing snd as a soundfont. \
3136 each inner list has the form: (name start loopstart loopend)"
3137 
3138   Xen outlist = Xen_empty_list;
3139   snd_info *sp;
3140   Snd_assert_sound(S_soundfont_info, snd, 1);
3141   sp = get_sp(snd);
3142   if (!sp)
3143     return(snd_no_such_sound_error(S_soundfont_info, snd));
3144   mus_header_read(sp->filename);
3145   if (mus_header_type() == MUS_SOUNDFONT)
3146     {
3147       int i, lim;
3148       lim = mus_header_sf2_entries();
3149       if (lim > 0)
3150 	for (i = lim - 1; i >= 0; i--)
3151 	  {
3152 	    Xen inlist;
3153 	    inlist = Xen_list_4(C_string_to_Xen_string(mus_header_sf2_name(i)),
3154 			        C_int_to_Xen_integer(mus_header_sf2_start(i)),
3155 			        C_int_to_Xen_integer(mus_header_sf2_loop_start(i)),
3156 			        C_int_to_Xen_integer(mus_header_sf2_end(i)));
3157 	    outlist = Xen_cons(inlist, outlist);
3158 	  }
3159     }
3160   return(outlist);
3161 }
3162 
3163 
find_sound_files_in_dir(const char * name)3164 dir_info *find_sound_files_in_dir(const char *name)
3165 {
3166   return(find_filtered_files_in_dir(name, JUST_SOUNDS_FILTER));
3167 }
3168 
3169 
g_sound_files_in_directory(Xen dirname)3170 static Xen g_sound_files_in_directory(Xen dirname)
3171 {
3172   #define H_sound_files_in_directory "(" S_sound_files_in_directory " :optional (directory \".\")): return a list of the sound files in 'directory'"
3173   char *name = NULL;
3174   Xen res = Xen_empty_list;
3175   Xen_check_type(Xen_is_string_or_unbound(dirname), dirname, 1, S_sound_files_in_directory, "a string");
3176   if (Xen_is_string(dirname))
3177     name = mus_expand_filename(Xen_string_to_C_string(dirname));
3178   else name = mus_expand_filename(".");
3179   if (name)
3180     {
3181       dir_info *dp = NULL;
3182       dp = find_sound_files_in_dir(name);
3183       if (dp)
3184 	{
3185 	  int i;
3186 	  for (i = dp->len - 1; i >= 0; i--)
3187 	    res = Xen_cons(C_string_to_Xen_string(dp->files[i]->filename), res);
3188 	  free_dir_info(dp);
3189 	}
3190       free(name);
3191     }
3192   return(res);
3193 }
3194 
3195 
3196 #define S_disk_kspace "disk-kspace"
3197 
g_disk_kspace(Xen name)3198 static Xen g_disk_kspace(Xen name)
3199 {
3200   #define H_disk_kspace "(" S_disk_kspace " filename): kbytes of space available on partition containing 'filename'"
3201   Xen_check_type(Xen_is_string(name), name, 1, S_disk_kspace, "a string");
3202   return(C_llong_to_Xen_llong(disk_kspace(Xen_string_to_C_string(name))));
3203 }
3204 
3205 
g_open_file_dialog(Xen managed)3206 static Xen g_open_file_dialog(Xen managed)
3207 {
3208   #define H_open_file_dialog "(" S_open_file_dialog " :optional (managed " PROC_TRUE ")): create the file dialog if needed and display it if 'managed'"
3209   Xen_check_type(Xen_is_boolean_or_unbound(managed), managed, 1, S_open_file_dialog, "a boolean");
3210   return(Xen_wrap_widget(make_open_file_dialog(FILE_READ_WRITE, (Xen_is_bound(managed)) ? Xen_boolean_to_C_bool(managed) : true)));
3211 }
3212 
3213 
g_mix_file_dialog(Xen managed)3214 static Xen g_mix_file_dialog(Xen managed)
3215 {
3216   #define H_mix_file_dialog "(" S_mix_file_dialog " :optional (managed " PROC_TRUE ")): create the mix file dialog if needed and display it if 'managed'"
3217   Xen_check_type(Xen_is_boolean_or_unbound(managed), managed, 1, S_mix_file_dialog, "a boolean");
3218   return(Xen_wrap_widget(make_mix_file_dialog((Xen_is_bound(managed)) ? Xen_boolean_to_C_bool(managed) : true)));
3219 }
3220 
3221 
g_insert_file_dialog(Xen managed)3222 static Xen g_insert_file_dialog(Xen managed)
3223 {
3224   #define H_insert_file_dialog "(" S_insert_file_dialog " :optional (managed " PROC_TRUE ")): create the insert file dialog if needed and display it if 'managed'"
3225   Xen_check_type(Xen_is_boolean_or_unbound(managed), managed, 1, S_insert_file_dialog, "a boolean");
3226   return(Xen_wrap_widget(make_insert_file_dialog((Xen_is_bound(managed)) ? Xen_boolean_to_C_bool(managed) : true)));
3227 }
3228 
3229 
g_edit_header_dialog(Xen snd_n)3230 static Xen g_edit_header_dialog(Xen snd_n)
3231 {
3232   #define H_edit_header_dialog "(" S_edit_header_dialog " :optional snd): start the Edit Header dialog on sound snd"
3233   snd_info *sp;
3234   sp = get_sp(snd_n);
3235   if ((!sp) || (sp->inuse != SOUND_NORMAL))
3236     return(snd_no_such_sound_error(S_edit_header_dialog, snd_n));
3237   return(Xen_wrap_widget(edit_header(sp)));
3238 }
3239 
3240 
g_save_selection_dialog(Xen managed)3241 static Xen g_save_selection_dialog(Xen managed)
3242 {
3243 #define H_save_selection_dialog "(" S_save_selection_dialog " :optional managed): start the Selection Save-as dialog"
3244   Xen_check_type(Xen_is_boolean_or_unbound(managed), managed, 1, S_save_selection_dialog, "a boolean");
3245   return(Xen_wrap_widget(make_selection_save_as_dialog(Xen_boolean_to_C_bool(managed))));
3246 }
3247 
3248 
g_save_region_dialog(Xen managed)3249 static Xen g_save_region_dialog(Xen managed)
3250 {
3251   #define H_save_region_dialog "(" S_save_region_dialog " :optional managed): start the Region Save-as dialog"
3252   Xen_check_type(Xen_is_boolean_or_unbound(managed), managed, 1, S_save_region_dialog, "a boolean");
3253   return(Xen_wrap_widget(make_region_save_as_dialog(Xen_boolean_to_C_bool(managed))));
3254 }
3255 
3256 
g_save_sound_dialog(Xen managed)3257 static Xen g_save_sound_dialog(Xen managed)
3258 {
3259   #define H_save_sound_dialog "(" S_save_sound_dialog " :optional managed): start the File Save-as dialog"
3260   Xen_check_type(Xen_is_boolean_or_unbound(managed), managed, 1, S_save_sound_dialog, "a boolean");
3261   return(Xen_wrap_widget(make_sound_save_as_dialog(Xen_boolean_to_C_bool(managed))));
3262 }
3263 
3264 
g_info_dialog(Xen subject,Xen msg)3265 static Xen g_info_dialog(Xen subject, Xen msg)
3266 {
3267   #define H_info_dialog "(" S_info_dialog " subject message): start the Info window with subject and message"
3268   Xen_check_type(Xen_is_string(subject), subject, 1, S_info_dialog, "a string");
3269   Xen_check_type(Xen_is_string(msg), msg, 2, S_info_dialog, "a string");
3270   return(Xen_wrap_widget(post_it(Xen_string_to_C_string(subject), Xen_string_to_C_string(msg))));
3271 }
3272 
3273 
g_new_sound_dialog(Xen managed)3274 static Xen g_new_sound_dialog(Xen managed)
3275 {
3276   #define H_new_sound_dialog "(" S_new_sound_dialog " :optional managed): start the File New sound dialog"
3277   Xen_check_type(Xen_is_boolean_or_unbound(managed), managed, 1, S_new_sound_dialog, "a boolean");
3278   return(Xen_wrap_widget(make_new_file_dialog(Xen_boolean_to_C_bool(managed))));
3279 }
3280 
3281 
3282 
3283 
g_is_sound_file(Xen name)3284 static Xen g_is_sound_file(Xen name)
3285 {
3286   #define H_is_sound_file "(" S_is_sound_file " name): " PROC_TRUE " if name has a known sound file extension"
3287   Xen_check_type(Xen_is_string(name), name, 1, S_is_sound_file, "a filename");
3288   return(C_bool_to_Xen_boolean(is_sound_file(Xen_string_to_C_string(name))));
3289 }
3290 
3291 
g_snd_tempnam(void)3292 static Xen g_snd_tempnam(void)
3293 {
3294   #define H_snd_tempnam "(" S_snd_tempnam "): return a new temp file name using " S_temp_dir "."
3295   char *tmp;
3296   Xen res;
3297   tmp = snd_tempnam();
3298   res = C_string_to_Xen_string(tmp);
3299   free(tmp);
3300   return(res);
3301 }
3302 
3303 
g_auto_update(void)3304 static Xen g_auto_update(void) {return(C_bool_to_Xen_boolean(auto_update(ss)));}
3305 
g_set_auto_update(Xen val)3306 static Xen g_set_auto_update(Xen val)
3307 {
3308   #define H_auto_update "(" S_auto_update "): " PROC_TRUE " if Snd should automatically update a file if it changes unexpectedly (default: " PROC_FALSE "). \
3309 The number of seconds between update checks is set by " S_auto_update_interval "."
3310   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_auto_update, "a boolean");
3311   set_auto_update(Xen_boolean_to_C_bool(val));
3312   return(C_bool_to_Xen_boolean(auto_update(ss)));
3313 }
3314 
3315 
g_auto_update_interval(void)3316 static Xen g_auto_update_interval(void) {return(C_double_to_Xen_real(auto_update_interval(ss)));}
3317 
g_set_auto_update_interval(Xen val)3318 static Xen g_set_auto_update_interval(Xen val)
3319 {
3320   mus_float_t ctime;
3321   #define H_auto_update_interval "(" S_auto_update_interval "): time (seconds) between background checks for changed file on disk (default: 60). \
3322 This value only matters if " S_auto_update " is " PROC_TRUE
3323 
3324   Xen_check_type(Xen_is_number(val), val, 1, S_set S_auto_update_interval, "a number");
3325   ctime = Xen_real_to_C_double(val);
3326   if (ctime != auto_update_interval(ss))
3327     {
3328       mus_float_t old_time;
3329       if ((ctime < 0.0) || (ctime > (24 * 3600)))
3330 	Xen_out_of_range_error(S_set S_auto_update_interval, 1, val, "invalid time");
3331 
3332       old_time = auto_update_interval(ss);
3333       set_auto_update_interval(ctime);
3334       /* if new value is 0.0, auto_update_check will notice that, and not run or re-start the update check */
3335       /* if new value is not 0.0, and old value was 0.0, we need to restart the timeout proc, unless it's still on the queue */
3336 
3337       if ((ctime > 0.0) && (old_time == 0.0))
3338 	auto_update_restart();
3339     }
3340   return(C_double_to_Xen_real(auto_update_interval(ss)));
3341 }
3342 
3343 
g_default_output_chans(void)3344 static Xen g_default_output_chans(void) {return(C_int_to_Xen_integer(default_output_chans(ss)));}
3345 
g_set_default_output_chans(Xen val)3346 static Xen g_set_default_output_chans(Xen val)
3347 {
3348   #define MAX_OUTPUT_CHANS 1024
3349   #define H_default_output_chans "(" S_default_output_chans "): default number of channels when a new or temporary file is created (1)"
3350   Xen_check_type(Xen_is_integer(val), val, 1, S_set S_default_output_chans, "an integer");
3351   set_default_output_chans(mus_iclamp(1, Xen_integer_to_C_int(val), MAX_OUTPUT_CHANS));
3352   return(C_int_to_Xen_integer(default_output_chans(ss)));
3353 }
3354 
3355 
g_default_output_srate(void)3356 static Xen g_default_output_srate(void) {return(C_int_to_Xen_integer(default_output_srate(ss)));}
3357 
g_set_default_output_srate(Xen val)3358 static Xen g_set_default_output_srate(Xen val)
3359 {
3360   #define MAX_OUTPUT_SRATE 1000000000
3361   #define H_default_output_srate "(" S_default_output_srate "): default srate when a new or temporary file is created (22050)"
3362 
3363   Xen_check_type(Xen_is_integer(val), val, 1, S_set S_default_output_srate, "an integer");
3364   set_default_output_srate(mus_iclamp(1, Xen_integer_to_C_int(val), MAX_OUTPUT_SRATE));
3365   return(C_int_to_Xen_integer(default_output_srate(ss)));
3366 }
3367 
3368 
g_default_output_header_type(void)3369 static Xen g_default_output_header_type(void) {return(C_int_to_Xen_integer((int)default_output_header_type(ss)));}
3370 
g_set_default_output_header_type(Xen val)3371 static Xen g_set_default_output_header_type(Xen val)
3372 {
3373   mus_header_t typ;
3374   #define H_default_output_header_type "(" S_default_output_header_type "): default header type when a new or temporary file is created. \
3375 Normally this is " S_mus_next "; -1 here indicates you want Snd to use the current sound's header type, if possible. \
3376 Other writable headers include " S_mus_aiff ", " S_mus_riff ", " S_mus_ircam ", " S_mus_nist ", " S_mus_aifc ", and " S_mus_raw "."
3377 
3378   Xen_check_type(Xen_is_integer(val), val, 1, S_set S_default_output_header_type, "an integer");
3379 
3380   typ = (mus_header_t)Xen_integer_to_C_int(val);
3381   if (mus_header_writable(typ, MUS_IGNORE_SAMPLE))
3382     set_default_output_header_type(typ);
3383   else Xen_out_of_range_error(S_set S_default_output_header_type, 1, val, "unwritable header type");
3384   return(C_int_to_Xen_integer((int)default_output_header_type(ss)));
3385 }
3386 
3387 
g_default_output_sample_type(void)3388 static Xen g_default_output_sample_type(void) {return(C_int_to_Xen_integer(default_output_sample_type(ss)));}
3389 
g_set_default_output_sample_type(Xen val)3390 static Xen g_set_default_output_sample_type(Xen val)
3391 {
3392   mus_sample_t samp_type;
3393   #define H_default_output_sample_type "(" S_default_output_sample_type "): default sample type when a new or temporary file is created, \
3394 normally " S_mus_ldouble "; mus-unknown-sample here means try to use the current sound's sample type; many other sample types \
3395 are available, but not all are compatible with all header types"
3396 
3397   Xen_check_type(Xen_is_integer(val), val, 1, S_set S_default_output_sample_type, "an integer");
3398 
3399   samp_type = (mus_sample_t)Xen_integer_to_C_int(val);
3400   if (mus_is_sample_type(samp_type))
3401     set_default_output_sample_type(samp_type);
3402   else Xen_out_of_range_error(S_set S_default_output_sample_type, 1, val, "unknown sample type");
3403   return(C_int_to_Xen_integer((int)default_output_sample_type(ss)));
3404 }
3405 
3406 
g_clipping(void)3407 static Xen g_clipping(void) {return(C_bool_to_Xen_boolean(clipping(ss)));}
3408 
g_set_clipping(Xen val)3409 static Xen g_set_clipping(Xen val)
3410 {
3411   #define H_clipping "(" S_clipping "): " PROC_TRUE " if Snd should clip output values to the current \
3412 output sample type's maximum. The default (" PROC_FALSE ") allows them to wrap-around which makes a very loud click"
3413   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_clipping, "a boolean");
3414   set_clipping(Xen_boolean_to_C_bool(val));
3415   return(C_bool_to_Xen_boolean(clipping(ss)));
3416 }
3417 
3418 
g_ask_before_overwrite(void)3419 static Xen g_ask_before_overwrite(void) {return(C_bool_to_Xen_boolean(ask_before_overwrite(ss)));}
3420 
g_set_ask_before_overwrite(Xen val)3421 static Xen g_set_ask_before_overwrite(Xen val)
3422 {
3423   #define H_ask_before_overwrite "(" S_ask_before_overwrite "): " PROC_TRUE " if you want Snd to ask before overwriting a file. \
3424 If " PROC_FALSE ", any existing file of the same name will be overwritten without warning when you save a sound."
3425   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_ask_before_overwrite, "a boolean");
3426   set_ask_before_overwrite(Xen_boolean_to_C_bool(val));
3427   return(C_bool_to_Xen_boolean(ask_before_overwrite(ss)));
3428 }
3429 
3430 
g_with_toolbar(void)3431 static Xen g_with_toolbar(void) {return(C_bool_to_Xen_boolean(with_toolbar(ss)));}
3432 
set_with_toolbar_and_display(bool val)3433 void set_with_toolbar_and_display(bool val)
3434 {
3435   set_with_toolbar(val);
3436 #if (!USE_NO_GUI)
3437   if (with_toolbar(ss))
3438     show_toolbar();
3439   else hide_toolbar();
3440 #endif
3441 }
3442 
g_set_with_toolbar(Xen val)3443 static Xen g_set_with_toolbar(Xen val)
3444 {
3445   #define H_with_toolbar "(" S_with_toolbar "): " PROC_TRUE " if you want a toolbar"
3446   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_with_toolbar, "a boolean");
3447   set_with_toolbar_and_display(Xen_boolean_to_C_bool(val));
3448   return(C_bool_to_Xen_boolean(with_toolbar(ss)));
3449 }
3450 
3451 
g_with_tooltips(void)3452 static Xen g_with_tooltips(void) {return(C_bool_to_Xen_boolean(with_tooltips(ss)));}
3453 
set_with_tooltips(bool val)3454 void set_with_tooltips(bool val)
3455 {
3456   in_set_with_tooltips(val);
3457 }
3458 
g_set_with_tooltips(Xen val)3459 static Xen g_set_with_tooltips(Xen val)
3460 {
3461   #define H_with_tooltips "(" S_with_tooltips "): " PROC_TRUE " if you want tooltips displayed at all"
3462   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_with_tooltips, "a boolean");
3463   set_with_tooltips(Xen_boolean_to_C_bool(val));
3464   return(C_bool_to_Xen_boolean(with_tooltips(ss)));
3465 }
3466 
3467 
set_with_menu_icons(bool val)3468 void set_with_menu_icons(bool val)
3469 {
3470   in_set_with_menu_icons(val);
3471 }
3472 
3473 
g_with_menu_icons(void)3474 static Xen g_with_menu_icons(void) {return(C_bool_to_Xen_boolean(with_menu_icons(ss)));}
3475 
g_set_with_menu_icons(Xen val)3476 static Xen g_set_with_menu_icons(Xen val)
3477 {
3478   #define H_with_menu_icons "(" S_with_menu_icons "): " PROC_TRUE " if you want icons in the menus"
3479   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_with_menu_icons, "a boolean");
3480   set_with_menu_icons(Xen_boolean_to_C_bool(val));
3481   return(C_bool_to_Xen_boolean(with_menu_icons(ss)));
3482 }
3483 
3484 
set_save_as_dialog_src(bool val)3485 static void set_save_as_dialog_src(bool val)
3486 {
3487   in_set_save_as_dialog_src(val);
3488   reflect_save_as_src(val);
3489 }
3490 
g_save_as_dialog_src(void)3491 static Xen g_save_as_dialog_src(void) {return(C_bool_to_Xen_boolean(save_as_dialog_src(ss)));}
3492 
g_set_save_as_dialog_src(Xen val)3493 static Xen g_set_save_as_dialog_src(Xen val)
3494 {
3495   #define H_save_as_dialog_src "(" S_save_as_dialog_src "): " PROC_TRUE " if you want the 'src' button set by default in the various Save-as dialogs"
3496   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_save_as_dialog_src, "a boolean");
3497   set_save_as_dialog_src(Xen_boolean_to_C_bool(val));
3498   return(C_bool_to_Xen_boolean(save_as_dialog_src(ss)));
3499 }
3500 
3501 
set_save_as_dialog_auto_comment(bool val)3502 static void set_save_as_dialog_auto_comment(bool val)
3503 {
3504   in_set_save_as_dialog_auto_comment(val);
3505   reflect_save_as_auto_comment(val);
3506 }
3507 
g_save_as_dialog_auto_comment(void)3508 static Xen g_save_as_dialog_auto_comment(void) {return(C_bool_to_Xen_boolean(save_as_dialog_auto_comment(ss)));}
3509 
g_set_save_as_dialog_auto_comment(Xen val)3510 static Xen g_set_save_as_dialog_auto_comment(Xen val)
3511 {
3512   #define H_save_as_dialog_auto_comment "(" S_save_as_dialog_auto_comment "): " PROC_TRUE " if you want the 'auto' button set by default in the various Save-as dialogs"
3513   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_save_as_dialog_auto_comment, "a boolean");
3514   set_save_as_dialog_auto_comment(Xen_boolean_to_C_bool(val));
3515   return(C_bool_to_Xen_boolean(save_as_dialog_auto_comment(ss)));
3516 }
3517 
3518 
g_remember_sound_state(void)3519 static Xen g_remember_sound_state(void) {return(C_bool_to_Xen_boolean(remember_sound_state(ss)));}
3520 
g_set_remember_sound_state(Xen val)3521 static Xen g_set_remember_sound_state(Xen val)
3522 {
3523   #define H_remember_sound_state "(" S_remember_sound_state "): " PROC_TRUE " if you want a Snd to remember the current \
3524 state of each sound when it is closed, restoring that state when it is opened again later."
3525   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_remember_sound_state, "a boolean");
3526   set_remember_sound_state(Xen_boolean_to_C_bool(val));
3527   return(C_bool_to_Xen_boolean(remember_sound_state(ss)));
3528 }
3529 
3530 
g_ask_about_unsaved_edits(void)3531 static Xen g_ask_about_unsaved_edits(void) {return(C_bool_to_Xen_boolean(ask_about_unsaved_edits(ss)));}
3532 
g_set_ask_about_unsaved_edits(Xen val)3533 static Xen g_set_ask_about_unsaved_edits(Xen val)
3534 {
3535   #define H_ask_about_unsaved_edits "(" S_ask_about_unsaved_edits "): " PROC_TRUE " if you want Snd to ask whether \
3536 to save unsaved edits when a sound is closed."
3537 
3538   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_ask_about_unsaved_edits, "a boolean");
3539   set_ask_about_unsaved_edits(Xen_boolean_to_C_bool(val));
3540   return(C_bool_to_Xen_boolean(ask_about_unsaved_edits(ss)));
3541 }
3542 
3543 
g_show_full_duration(void)3544 static Xen g_show_full_duration(void) {return(C_bool_to_Xen_boolean(show_full_duration(ss)));}
3545 
g_set_show_full_duration(Xen val)3546 static Xen g_set_show_full_duration(Xen val)
3547 {
3548   int i;
3549   #define H_show_full_duration "(" S_show_full_duration "): " PROC_TRUE " if you want the entire sound \
3550 displayed whn it is opened."
3551 
3552   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_show_full_duration, "a boolean");
3553   set_show_full_duration(Xen_boolean_to_C_bool(val));
3554 
3555   for (i = 0; i < ss->max_sounds; i++)
3556     {
3557       snd_info *sp;
3558       sp = ss->sounds[i];
3559       if ((sp) && (sp->inuse == SOUND_NORMAL))
3560 	{
3561 	  uint32_t j;
3562 	  for (j = 0; j < sp->nchans; j++)
3563 	    set_x_axis_x0x1(sp->chans[j], 0.0, sp->chans[j]->axis->xmax);
3564 	}
3565     }
3566 
3567   return(C_bool_to_Xen_boolean(show_full_duration(ss)));
3568 }
3569 
3570 
g_initial_beg(void)3571 static Xen g_initial_beg(void) {return(C_double_to_Xen_real(initial_beg(ss)));}
3572 
g_set_initial_beg(Xen val)3573 static Xen g_set_initial_beg(Xen val)
3574 {
3575   #define H_initial_beg "(" S_initial_beg "): the begin point (in seconds) for the initial graph of a sound."
3576 
3577   Xen_check_type(Xen_is_number(val), val, 1, S_set S_initial_beg, "a number");
3578   set_initial_beg(Xen_real_to_C_double(val));
3579   return(C_double_to_Xen_real(initial_beg(ss)));
3580 }
3581 
3582 
g_initial_dur(void)3583 static Xen g_initial_dur(void) {return(C_double_to_Xen_real(initial_dur(ss)));}
3584 
g_set_initial_dur(Xen val)3585 static Xen g_set_initial_dur(Xen val)
3586 {
3587   #define H_initial_dur "(" S_initial_dur "): the duration (in seconds) for the initial graph of a sound."
3588 
3589   Xen_check_type(Xen_is_number(val), val, 1, S_set S_initial_dur, "a number");
3590   set_initial_dur(Xen_real_to_C_double(val));
3591   return(C_double_to_Xen_real(initial_dur(ss)));
3592 }
3593 
3594 
g_show_full_range(void)3595 static Xen g_show_full_range(void) {return(C_bool_to_Xen_boolean(show_full_range(ss)));}
3596 
g_set_show_full_range(Xen val)3597 static Xen g_set_show_full_range(Xen val)
3598 {
3599   int i;
3600   #define H_show_full_range "(" S_show_full_range "): " PROC_TRUE " if you want the graph y-bounds to accommodate the sound's \
3601 max and min when it is opened."
3602 
3603   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_show_full_range, "a boolean");
3604   set_show_full_range(Xen_boolean_to_C_bool(val));
3605 
3606   for (i = 0; i < ss->max_sounds; i++)
3607     {
3608       snd_info *sp;
3609       sp = ss->sounds[i];
3610       if ((sp) && (sp->inuse == SOUND_NORMAL))
3611 	{
3612 	  uint32_t j;
3613 	  for (j = 0; j < sp->nchans; j++)
3614 	    {
3615 	      chan_info *cp;
3616 	      axis_info *ap;
3617 	      mus_float_t hi;
3618 	      cp = sp->chans[j];
3619 	      ap = cp->axis;
3620 	      hi = channel_maxamp(cp, AT_CURRENT_EDIT_POSITION);
3621 	      if (hi > ap->ymax)
3622 		{
3623 		  ap->ymin = -hi;
3624 		  ap->ymax = hi;
3625 		  ap->y_ambit = 2 * hi;
3626 		  ap->y0 = -hi;
3627 		  ap->y1 = hi;
3628 		  ap->zy = 1.0;
3629 		  ap->sy = 0.0;
3630 		  resize_sy_and_zy(cp);
3631 		  apply_y_axis_change(cp);
3632 		}
3633 	    }
3634 	}
3635     }
3636 
3637   return(C_bool_to_Xen_boolean(show_full_range(ss)));
3638 }
3639 
3640 
3641 
3642 
Xen_wrap_1_arg(g_add_sound_file_extension_w,g_add_sound_file_extension)3643 Xen_wrap_1_arg(g_add_sound_file_extension_w, g_add_sound_file_extension)
3644 Xen_wrap_no_args(g_sound_file_extensions_w, g_sound_file_extensions)
3645 Xen_wrap_1_arg(g_set_sound_file_extensions_w, g_set_sound_file_extensions)
3646 Xen_wrap_1_arg(g_file_write_date_w, g_file_write_date)
3647 Xen_wrap_1_optional_arg(g_soundfont_info_w, g_soundfont_info)
3648 Xen_wrap_1_optional_arg(g_sound_files_in_directory_w, g_sound_files_in_directory)
3649 Xen_wrap_1_optional_arg(g_sound_loop_info_w, g_sound_loop_info)
3650 Xen_wrap_2_optional_args(g_set_sound_loop_info_w, g_set_sound_loop_info)
3651 Xen_wrap_1_arg(g_disk_kspace_w, g_disk_kspace)
3652 Xen_wrap_1_optional_arg(g_open_file_dialog_w, g_open_file_dialog)
3653 Xen_wrap_1_optional_arg(g_mix_file_dialog_w, g_mix_file_dialog)
3654 Xen_wrap_1_optional_arg(g_insert_file_dialog_w, g_insert_file_dialog)
3655 Xen_wrap_1_optional_arg(g_edit_header_dialog_w, g_edit_header_dialog)
3656 Xen_wrap_1_optional_arg(g_save_selection_dialog_w, g_save_selection_dialog)
3657 Xen_wrap_1_optional_arg(g_save_region_dialog_w, g_save_region_dialog)
3658 Xen_wrap_1_optional_arg(g_save_sound_dialog_w, g_save_sound_dialog)
3659 Xen_wrap_1_optional_arg(g_new_sound_dialog_w, g_new_sound_dialog)
3660 Xen_wrap_2_args(g_info_dialog_w, g_info_dialog)
3661 Xen_wrap_1_arg(g_is_sound_file_w, g_is_sound_file)
3662 Xen_wrap_no_args(g_snd_tempnam_w, g_snd_tempnam)
3663 Xen_wrap_no_args(g_auto_update_w, g_auto_update)
3664 Xen_wrap_1_arg(g_set_auto_update_w, g_set_auto_update)
3665 Xen_wrap_no_args(g_auto_update_interval_w, g_auto_update_interval)
3666 Xen_wrap_1_arg(g_set_auto_update_interval_w, g_set_auto_update_interval)
3667 Xen_wrap_no_args(g_default_output_chans_w, g_default_output_chans)
3668 Xen_wrap_1_arg(g_set_default_output_chans_w, g_set_default_output_chans)
3669 Xen_wrap_no_args(g_default_output_srate_w, g_default_output_srate)
3670 Xen_wrap_1_arg(g_set_default_output_srate_w, g_set_default_output_srate)
3671 Xen_wrap_no_args(g_default_output_header_type_w, g_default_output_header_type)
3672 Xen_wrap_1_arg(g_set_default_output_header_type_w, g_set_default_output_header_type)
3673 Xen_wrap_no_args(g_default_output_sample_type_w, g_default_output_sample_type)
3674 Xen_wrap_1_arg(g_set_default_output_sample_type_w, g_set_default_output_sample_type)
3675 Xen_wrap_no_args(g_ask_before_overwrite_w, g_ask_before_overwrite)
3676 Xen_wrap_1_arg(g_set_ask_before_overwrite_w, g_set_ask_before_overwrite)
3677 Xen_wrap_no_args(g_with_toolbar_w, g_with_toolbar)
3678 Xen_wrap_1_arg(g_set_with_toolbar_w, g_set_with_toolbar)
3679 Xen_wrap_no_args(g_with_tooltips_w, g_with_tooltips)
3680 Xen_wrap_1_arg(g_set_with_tooltips_w, g_set_with_tooltips)
3681 Xen_wrap_no_args(g_with_menu_icons_w, g_with_menu_icons)
3682 Xen_wrap_1_arg(g_set_with_menu_icons_w, g_set_with_menu_icons)
3683 Xen_wrap_no_args(g_save_as_dialog_src_w, g_save_as_dialog_src)
3684 Xen_wrap_1_arg(g_set_save_as_dialog_src_w, g_set_save_as_dialog_src)
3685 Xen_wrap_no_args(g_save_as_dialog_auto_comment_w, g_save_as_dialog_auto_comment)
3686 Xen_wrap_1_arg(g_set_save_as_dialog_auto_comment_w, g_set_save_as_dialog_auto_comment)
3687 Xen_wrap_no_args(g_remember_sound_state_w, g_remember_sound_state)
3688 Xen_wrap_1_arg(g_set_remember_sound_state_w, g_set_remember_sound_state)
3689 Xen_wrap_no_args(g_ask_about_unsaved_edits_w, g_ask_about_unsaved_edits)
3690 Xen_wrap_1_arg(g_set_ask_about_unsaved_edits_w, g_set_ask_about_unsaved_edits)
3691 Xen_wrap_no_args(g_show_full_duration_w, g_show_full_duration)
3692 Xen_wrap_1_arg(g_set_show_full_duration_w, g_set_show_full_duration)
3693 Xen_wrap_no_args(g_show_full_range_w, g_show_full_range)
3694 Xen_wrap_1_arg(g_set_show_full_range_w, g_set_show_full_range)
3695 Xen_wrap_no_args(g_initial_beg_w, g_initial_beg)
3696 Xen_wrap_1_arg(g_set_initial_beg_w, g_set_initial_beg)
3697 Xen_wrap_no_args(g_initial_dur_w, g_initial_dur)
3698 Xen_wrap_1_arg(g_set_initial_dur_w, g_set_initial_dur)
3699 Xen_wrap_no_args(g_clipping_w, g_clipping)
3700 Xen_wrap_1_arg(g_set_clipping_w, g_set_clipping)
3701 Xen_wrap_1_arg(g_delete_file_filter_w, g_delete_file_filter)
3702 Xen_wrap_2_args(g_add_file_filter_w, g_add_file_filter)
3703 
3704 
3705 #if HAVE_SCHEME
3706 static s7_pointer acc_default_output_header_type(s7_scheme *sc, s7_pointer args) {return(g_set_default_output_header_type(s7_cadr(args)));}
acc_default_output_sample_type(s7_scheme * sc,s7_pointer args)3707 static s7_pointer acc_default_output_sample_type(s7_scheme *sc, s7_pointer args) {return(g_set_default_output_sample_type(s7_cadr(args)));}
acc_default_output_chans(s7_scheme * sc,s7_pointer args)3708 static s7_pointer acc_default_output_chans(s7_scheme *sc, s7_pointer args) {return(g_set_default_output_chans(s7_cadr(args)));}
acc_default_output_srate(s7_scheme * sc,s7_pointer args)3709 static s7_pointer acc_default_output_srate(s7_scheme *sc, s7_pointer args) {return(g_set_default_output_srate(s7_cadr(args)));}
acc_ask_before_overwrite(s7_scheme * sc,s7_pointer args)3710 static s7_pointer acc_ask_before_overwrite(s7_scheme *sc, s7_pointer args) {return(g_set_ask_before_overwrite(s7_cadr(args)));}
acc_ask_about_unsaved_edits(s7_scheme * sc,s7_pointer args)3711 static s7_pointer acc_ask_about_unsaved_edits(s7_scheme *sc, s7_pointer args) {return(g_set_ask_about_unsaved_edits(s7_cadr(args)));}
acc_show_full_duration(s7_scheme * sc,s7_pointer args)3712 static s7_pointer acc_show_full_duration(s7_scheme *sc, s7_pointer args) {return(g_set_show_full_duration(s7_cadr(args)));}
acc_show_full_range(s7_scheme * sc,s7_pointer args)3713 static s7_pointer acc_show_full_range(s7_scheme *sc, s7_pointer args) {return(g_set_show_full_range(s7_cadr(args)));}
acc_remember_sound_state(s7_scheme * sc,s7_pointer args)3714 static s7_pointer acc_remember_sound_state(s7_scheme *sc, s7_pointer args) {return(g_set_remember_sound_state(s7_cadr(args)));}
acc_save_as_dialog_src(s7_scheme * sc,s7_pointer args)3715 static s7_pointer acc_save_as_dialog_src(s7_scheme *sc, s7_pointer args) {return(g_set_save_as_dialog_src(s7_cadr(args)));}
acc_save_as_dialog_auto_comment(s7_scheme * sc,s7_pointer args)3716 static s7_pointer acc_save_as_dialog_auto_comment(s7_scheme *sc, s7_pointer args) {return(g_set_save_as_dialog_auto_comment(s7_cadr(args)));}
acc_with_toolbar(s7_scheme * sc,s7_pointer args)3717 static s7_pointer acc_with_toolbar(s7_scheme *sc, s7_pointer args) {return(g_set_with_toolbar(s7_cadr(args)));}
acc_with_tooltips(s7_scheme * sc,s7_pointer args)3718 static s7_pointer acc_with_tooltips(s7_scheme *sc, s7_pointer args) {return(g_set_with_tooltips(s7_cadr(args)));}
acc_with_menu_icons(s7_scheme * sc,s7_pointer args)3719 static s7_pointer acc_with_menu_icons(s7_scheme *sc, s7_pointer args) {return(g_set_with_menu_icons(s7_cadr(args)));}
acc_initial_beg(s7_scheme * sc,s7_pointer args)3720 static s7_pointer acc_initial_beg(s7_scheme *sc, s7_pointer args) {return(g_set_initial_beg(s7_cadr(args)));}
acc_initial_dur(s7_scheme * sc,s7_pointer args)3721 static s7_pointer acc_initial_dur(s7_scheme *sc, s7_pointer args) {return(g_set_initial_dur(s7_cadr(args)));}
acc_auto_update(s7_scheme * sc,s7_pointer args)3722 static s7_pointer acc_auto_update(s7_scheme *sc, s7_pointer args) {return(g_set_auto_update(s7_cadr(args)));}
acc_auto_update_interval(s7_scheme * sc,s7_pointer args)3723 static s7_pointer acc_auto_update_interval(s7_scheme *sc, s7_pointer args) {return(g_set_auto_update_interval(s7_cadr(args)));}
acc_clipping(s7_scheme * sc,s7_pointer args)3724 static s7_pointer acc_clipping(s7_scheme *sc, s7_pointer args) {return(g_set_clipping(s7_cadr(args)));}
3725 #endif
3726 
3727 
g_init_file(void)3728 void g_init_file(void)
3729 {
3730 #if HAVE_SCHEME
3731   s7_pointer pl_b, pl_bb, pl_i, pl_ii, pl_s, pl_ss, pl_d, pl_dr, pl_zb, pl_l, pl_ll, pl_bs, pl_is, pl_lt, pl_zt, pl_zss, pl_ltl, pl_ls, pl_isf;
3732   {
3733     s7_pointer i, b, d, r, s, p, l, t, z;
3734     i = s7_make_symbol(s7, "integer?");
3735     b = s7_make_symbol(s7, "boolean?");
3736     d = s7_make_symbol(s7, "float?");
3737     r = s7_make_symbol(s7, "real?");
3738     s = s7_make_symbol(s7, "string?");
3739     p = s7_make_symbol(s7, "pair?");
3740     l = s7_make_symbol(s7, "list?");
3741     t = s7_t(s7);
3742 
3743     z = s7_make_signature(s7, 2, b, p);
3744     pl_b = s7_make_signature(s7, 1, b);
3745     pl_bb = s7_make_signature(s7, 2, b, b);
3746     pl_bs = s7_make_signature(s7, 2, b, s);
3747     pl_i = s7_make_signature(s7, 1, i);
3748     pl_ii = s7_make_signature(s7, 2, i, i);
3749     pl_is = s7_make_signature(s7, 2, i, s);
3750     pl_s = s7_make_signature(s7, 1, s);
3751     pl_ss = s7_make_signature(s7, 2, s, s);
3752     pl_d = s7_make_signature(s7, 1, d);
3753     pl_dr = s7_make_signature(s7, 2, d, r);
3754     pl_zb = s7_make_signature(s7, 2, z, b);
3755     pl_zt = s7_make_signature(s7, 2, z, t);
3756     pl_zss = s7_make_signature(s7, 3, z, s, s);
3757     pl_l = s7_make_signature(s7, 1, l);
3758     pl_ll = s7_make_signature(s7, 2, l, l);
3759     pl_lt = s7_make_signature(s7, 2, l, t);
3760     pl_ls = s7_make_signature(s7, 2, l, s);
3761     pl_ltl = s7_make_signature(s7, 3, l, t, l);
3762     pl_isf = s7_make_signature(s7, 3, i, s, s7_make_symbol(s7, "procedure?"));
3763   }
3764 #endif
3765 
3766   Xen_define_typed_procedure(S_add_sound_file_extension, g_add_sound_file_extension_w, 1, 0, 0, H_add_sound_file_extension, pl_ss);
3767   Xen_define_typed_dilambda(S_sound_file_extensions, g_sound_file_extensions_w, H_sound_file_extensions,
3768 			    S_set S_sound_file_extensions, g_set_sound_file_extensions_w,  0, 0, 1, 0, pl_l, pl_ll);
3769 
3770   Xen_define_typed_procedure(S_is_sound_file,              g_is_sound_file_w,              1, 0, 0, H_is_sound_file,	    pl_bs);
3771   Xen_define_typed_procedure(S_file_write_date,            g_file_write_date_w,            1, 0, 0, H_file_write_date,	    pl_is);
3772   Xen_define_typed_procedure(S_soundfont_info,             g_soundfont_info_w,             0, 1, 0, H_soundfont_info,	    pl_lt);
3773   Xen_define_typed_procedure(S_sound_files_in_directory,   g_sound_files_in_directory_w,   0, 1, 0, H_sound_files_in_directory, pl_ls);
3774   Xen_define_typed_procedure(S_disk_kspace,                g_disk_kspace_w,                1, 0, 0, H_disk_kspace,	    pl_is);
3775 
3776   Xen_define_typed_procedure(S_open_file_dialog,           g_open_file_dialog_w,           0, 1, 0, H_open_file_dialog,     pl_zb);
3777   Xen_define_typed_procedure(S_mix_file_dialog,            g_mix_file_dialog_w,            0, 1, 0, H_mix_file_dialog,	    pl_zb);
3778   Xen_define_typed_procedure(S_insert_file_dialog,         g_insert_file_dialog_w,         0, 1, 0, H_insert_file_dialog,   pl_zb);
3779   Xen_define_typed_procedure(S_edit_header_dialog,         g_edit_header_dialog_w,         0, 1, 0, H_edit_header_dialog,   pl_zt);
3780   Xen_define_typed_procedure(S_save_selection_dialog,      g_save_selection_dialog_w,      0, 1, 0, H_save_selection_dialog, pl_zb);
3781   Xen_define_typed_procedure(S_save_region_dialog,         g_save_region_dialog_w,         0, 1, 0, H_save_region_dialog,   pl_zb);
3782   Xen_define_typed_procedure(S_save_sound_dialog,          g_save_sound_dialog_w,          0, 1, 0, H_save_sound_dialog,    pl_zb);
3783   Xen_define_typed_procedure(S_new_sound_dialog,           g_new_sound_dialog_w,           0, 1, 0, H_new_sound_dialog,     pl_zb);
3784   Xen_define_typed_procedure(S_info_dialog,                g_info_dialog_w,                2, 0, 0, H_info_dialog,          pl_zss);
3785 
3786   Xen_define_typed_dilambda(S_sound_loop_info,      g_sound_loop_info_w, H_sound_loop_info,
3787 			    S_set S_sound_loop_info, g_set_sound_loop_info_w,  0, 1, 1, 1, pl_lt, pl_ltl);
3788 
3789   Xen_define_variable(S_snd_opened_sound, snd_opened_sound, Xen_false);
3790 
3791   #define H_open_hook S_open_hook " (name): called each time a file is opened (before the actual open). \
3792 If it returns " PROC_TRUE ", the file is not opened."
3793 
3794   #define H_before_close_hook S_before_close_hook " (snd): called each time a file is closed (before the close). \
3795 If it returns " PROC_TRUE ", the file is not closed."
3796 
3797   #define H_close_hook S_close_hook " (snd): called each time a file is closed (before the close)."
3798 
3799   #define H_bad_header_hook S_bad_header_hook " (name): called if a file has some bogus-looking header. \
3800 Return " PROC_TRUE " to give up on that file."
3801 
3802   #define H_after_save_as_hook S_after_save_as_hook " (snd name dialog): called \
3803 upon File:Save as or " S_save_sound_as " completion."
3804 
3805   #define H_before_save_as_hook S_before_save_as_hook " (snd name selection sampling-rate sample-type header-type comment): called \
3806 before File:Save as or " S_save_sound_as ". Provides a way to fixup a sound just before it is saved."
3807 
3808   #define H_during_open_hook S_during_open_hook " (fd name reason): called after file is opened, but before data has been read."
3809 
3810 #if HAVE_SCHEME
3811   #define H_after_open_hook S_after_open_hook " (snd): called just before the new file's window is displayed. \
3812 This provides a way to set various sound-specific defaults. \n\
3813   (hook-push " S_after_open_hook "\n\
3814     (lambda (snd) \n\
3815       (if (> (" S_channels " snd) 1) \n\
3816           (set! (" S_channel_style " snd) " S_channels_combined "))))"
3817 #endif
3818 
3819 #if HAVE_RUBY
3820   #define H_after_open_hook S_after_open_hook " (snd): called just before the new file's window is displayed. \
3821 This provides a way to set various sound-specific defaults. \n\
3822   $after_open_hook.add-hook!(\"set-channels-combined\") do |snd| \n\
3823     if (channels(snd) > 1) \n\
3824       set_channel_style(Channels_combined, snd)\n\
3825     end\n\
3826   end"
3827 #endif
3828 
3829 #if HAVE_FORTH
3830   #define H_after_open_hook S_after_open_hook " (snd): called just before the new file's window is displayed. \
3831 This provides a way to set various sound-specific defaults. \n\
3832 " S_after_open_hook " lambda: <{ snd }>\n\
3833   snd " S_channels " 1 > if\n\
3834     " S_channels_combined " snd set-" S_channel_style "\n\
3835   else\n\
3836     #f\n\
3837   then\n\
3838 ; add-hook!"
3839 #endif
3840 
3841   open_hook =           Xen_define_hook(S_open_hook,           "(make-hook 'name)",                1, H_open_hook);
3842   before_close_hook =   Xen_define_hook(S_before_close_hook,   "(make-hook 'snd)",                 1, H_before_close_hook);
3843   close_hook =          Xen_define_hook(S_close_hook,          "(make-hook 'snd)",                 1, H_close_hook);
3844   bad_header_hook =     Xen_define_hook(S_bad_header_hook,     "(make-hook 'name)",                1, H_bad_header_hook);
3845   after_save_as_hook =  Xen_define_hook(S_after_save_as_hook,  "(make-hook 'snd 'name 'dialog)",   3, H_after_save_as_hook);
3846   before_save_as_hook = Xen_define_hook(S_before_save_as_hook, "(make-hook 'snd 'name 'selection 'sampling-rate 'sample-type 'header-type 'comment)", 7, H_before_save_as_hook);
3847   during_open_hook =    Xen_define_hook(S_during_open_hook,    "(make-hook 'fd 'name 'reason)",    3, H_during_open_hook);
3848   after_open_hook =     Xen_define_hook(S_after_open_hook,     "(make-hook 'snd)",                 1, H_after_open_hook);
3849 
3850   #define H_open_raw_sound_hook S_open_raw_sound_hook " (name state): called when a headerless sound file is opened. \
3851 Its result can be a list describing the raw file's attributes (thereby bypassing the Raw File Dialog and so on). \
3852 The list is interpreted as (list chans srate sample-type data-location data-length) where trailing elements can \
3853 be omitted (location defaults to 0, and length defaults to the file length in bytes)."
3854 
3855   open_raw_sound_hook = Xen_define_hook(S_open_raw_sound_hook, "(make-hook 'name 'state)", 2, H_open_raw_sound_hook);
3856 
3857   #define H_update_hook S_update_hook " (snd): called just before " S_update_sound " is called. \
3858 The update process can  be triggered by a variety of situations, not just by " S_update_sound ". \
3859 The hook is passed the sound's index.  If it returns " PROC_TRUE ", the update is cancelled (this is not \
3860 recommended!); if it returns a procedure of one argument, that procedure is called upon \
3861 completion of the update operation; its argument is the (possibly different) sound index. \
3862 Snd tries to maintain the index across the update, but if you change the number of channels \
3863 the newly updated sound may have a different index."
3864 
3865   update_hook = Xen_define_hook(S_update_hook, "(make-hook 'snd)", 1, H_update_hook);
3866 
3867   Xen_define_typed_procedure(S_snd_tempnam,        g_snd_tempnam_w,        0, 0, 0, H_snd_tempnam, pl_s);
3868 
3869   Xen_define_typed_dilambda(S_auto_update, g_auto_update_w, H_auto_update,
3870 			    S_set S_auto_update, g_set_auto_update_w,  0, 0, 1, 0, pl_b, pl_bb);
3871 
3872   Xen_define_typed_dilambda(S_auto_update_interval, g_auto_update_interval_w, H_auto_update_interval,
3873 			    S_set S_auto_update_interval, g_set_auto_update_interval_w,  0, 0, 1, 0, pl_d, pl_dr);
3874 
3875   Xen_define_typed_dilambda(S_ask_before_overwrite, g_ask_before_overwrite_w, H_ask_before_overwrite,
3876 			    S_set S_ask_before_overwrite, g_set_ask_before_overwrite_w,  0, 0, 1, 0, pl_b, pl_bb);
3877 
3878   Xen_define_typed_dilambda(S_with_toolbar, g_with_toolbar_w, H_with_toolbar,
3879 			    S_set S_with_toolbar, g_set_with_toolbar_w,  0, 0, 1, 0, pl_b, pl_bb);
3880 
3881   Xen_define_typed_dilambda(S_with_tooltips, g_with_tooltips_w, H_with_tooltips,
3882 			    S_set S_with_tooltips, g_set_with_tooltips_w,  0, 0, 1, 0, pl_b, pl_bb);
3883 
3884   Xen_define_typed_dilambda(S_with_menu_icons, g_with_menu_icons_w, H_with_menu_icons,
3885 			    S_set S_with_menu_icons, g_set_with_menu_icons_w,  0, 0, 1, 0, pl_b, pl_bb);
3886 
3887   Xen_define_typed_dilambda(S_save_as_dialog_src, g_save_as_dialog_src_w, H_save_as_dialog_src,
3888 			    S_set S_save_as_dialog_src, g_set_save_as_dialog_src_w,  0, 0, 1, 0, pl_b, pl_bb);
3889 
3890   Xen_define_typed_dilambda(S_save_as_dialog_auto_comment, g_save_as_dialog_auto_comment_w, H_save_as_dialog_auto_comment,
3891 			    S_set S_save_as_dialog_auto_comment, g_set_save_as_dialog_auto_comment_w,  0, 0, 1, 0, pl_b, pl_bb);
3892 
3893   Xen_define_typed_dilambda(S_remember_sound_state, g_remember_sound_state_w, H_remember_sound_state,
3894 			    S_set S_remember_sound_state, g_set_remember_sound_state_w,  0, 0, 1, 0, pl_b, pl_bb);
3895 
3896   Xen_define_typed_dilambda(S_ask_about_unsaved_edits, g_ask_about_unsaved_edits_w, H_ask_about_unsaved_edits,
3897 			    S_set S_ask_about_unsaved_edits, g_set_ask_about_unsaved_edits_w,  0, 0, 1, 0, pl_b, pl_bb);
3898 
3899   Xen_define_typed_dilambda(S_show_full_duration, g_show_full_duration_w, H_show_full_duration,
3900 			    S_set S_show_full_duration, g_set_show_full_duration_w,  0, 0, 1, 0, pl_b, pl_bb);
3901 
3902   Xen_define_typed_dilambda(S_show_full_range, g_show_full_range_w, H_show_full_range,
3903 			    S_set S_show_full_range, g_set_show_full_range_w,  0, 0, 1, 0, pl_b, pl_bb);
3904 
3905   Xen_define_typed_dilambda(S_initial_beg, g_initial_beg_w, H_initial_beg,
3906 			    S_set S_initial_beg, g_set_initial_beg_w,  0, 0, 1, 0, pl_d, pl_dr);
3907 
3908   Xen_define_typed_dilambda(S_initial_dur, g_initial_dur_w, H_initial_dur,
3909 			    S_set S_initial_dur, g_set_initial_dur_w,  0, 0, 1, 0, pl_d, pl_dr);
3910 
3911   Xen_define_typed_dilambda(S_default_output_chans, g_default_output_chans_w, H_default_output_chans,
3912 			    S_set S_default_output_chans, g_set_default_output_chans_w,  0, 0, 1, 0, pl_i, pl_ii);
3913 
3914   Xen_define_typed_dilambda(S_default_output_srate, g_default_output_srate_w, H_default_output_srate,
3915 			    S_set S_default_output_srate, g_set_default_output_srate_w,  0, 0, 1, 0, pl_i, pl_ii);
3916 
3917   Xen_define_typed_dilambda(S_default_output_header_type, g_default_output_header_type_w, H_default_output_header_type,
3918 			    S_set S_default_output_header_type, g_set_default_output_header_type_w,  0, 0, 1, 0, pl_i, pl_ii);
3919 
3920   Xen_define_typed_dilambda(S_default_output_sample_type, g_default_output_sample_type_w, H_default_output_sample_type,
3921 			    S_set S_default_output_sample_type, g_set_default_output_sample_type_w,  0, 0, 1, 0, pl_i, pl_ii);
3922   Xen_define_typed_dilambda(S_clipping, g_clipping_w, H_clipping,
3923 			    S_set S_clipping, g_set_clipping_w,  0, 0, 1, 0, pl_b, pl_bb);
3924 
3925   Xen_define_typed_procedure(S_add_file_filter,    g_add_file_filter_w,    2, 0, 0, H_add_file_filter, pl_isf);
3926   Xen_define_typed_procedure(S_delete_file_filter, g_delete_file_filter_w, 1, 0, 0, H_delete_file_filter, pl_ii);
3927 
3928   ss->file_filters_size = INITIAL_FILE_FILTERS_SIZE;
3929   ss->file_filters = Xen_make_vector(ss->file_filters_size, Xen_false);
3930   Xen_GC_protect(ss->file_filters);
3931 
3932 #if HAVE_SCHEME
3933   s7_set_documentation(s7, ss->default_output_header_type_symbol, "*default-output-header-type*: header type when a new file is created (mus-next etc)");
3934   s7_set_documentation(s7, ss->default_output_sample_type_symbol, "*default-output-sample-type*: sample type when a new file is created (mus-ldouble etc)");
3935   s7_set_documentation(s7, ss->default_output_chans_symbol, "*default-output-chans*: number of channels when a new file is created (1)");
3936   s7_set_documentation(s7, ss->default_output_srate_symbol, "*default-output-srate*: sampling rate when a new file is created (44100)");
3937   s7_set_documentation(s7, ss->ask_before_overwrite_symbol, "*ask-before-overwrite*: #t if you want Snd to ask before overwriting a file.");
3938   s7_set_documentation(s7, ss->ask_about_unsaved_edits_symbol, "*ask-about-unsaved-edits*: #t if you want Snd to ask whether to save unsaved edits when a sound is closed.");
3939   s7_set_documentation(s7, ss->show_full_duration_symbol, "*show-full-duration*: #t if you want the entire sound displayed whn it is opened.");
3940   s7_set_documentation(s7, ss->show_full_range_symbol, "*show-full-range*: #t if you want the graph y-bounds to accommodate the sound's max and min when it is opened.");
3941   s7_set_documentation(s7, ss->remember_sound_state_symbol, "*remember-sound-state*: #t if you want a Snd to remember the current state of each sound when it is closed, restoring that state when it is opened again later.");
3942   s7_set_documentation(s7, ss->save_as_dialog_src_symbol, "*save-as-dialog-src*: #t if you want the 'src' button set by default in the various Save-as dialogs");
3943   s7_set_documentation(s7, ss->save_as_dialog_auto_comment_symbol, "*save-as-dialog-auto-comment*: #t if you want the 'auto' button set by default in the various Save-as dialogs");
3944   s7_set_documentation(s7, ss->with_toolbar_symbol, "*with-toolbar*: #t if you want a toolbar");
3945   s7_set_documentation(s7, ss->with_tooltips_symbol, "*with-tooltips*: #t if you want tooltips");
3946   s7_set_documentation(s7, ss->with_menu_icons_symbol, "*with-menu-icons*: #t if you want icons in the menus");
3947   s7_set_documentation(s7, ss->initial_beg_symbol, "*initial-beg*: the begin point (in seconds) for the initial graph of a sound.");
3948   s7_set_documentation(s7, ss->initial_dur_symbol, "*initial-dur*: the duration (in seconds) for the initial graph of a sound.");
3949   s7_set_documentation(s7, ss->auto_update_symbol, "*auto-update*: #t if Snd should automatically update a file if it changes unexpectedly");
3950   s7_set_documentation(s7, ss->auto_update_interval_symbol, "*auto-update-interval*: time (seconds) between background checks for changed file on disk (60)");
3951   s7_set_documentation(s7, ss->clipping_symbol, "*clipping*: #t if Snd should clip output values");
3952 
3953 
3954   s7_set_setter(s7, ss->default_output_header_type_symbol, s7_make_function(s7, "[acc-" S_default_output_header_type "]", acc_default_output_header_type, 2, 0, false, "accessor"));
3955   s7_set_setter(s7, ss->default_output_sample_type_symbol, s7_make_function(s7, "[acc-" S_default_output_sample_type "]", acc_default_output_sample_type, 2, 0, false, "accessor"));
3956   s7_set_setter(s7, ss->default_output_chans_symbol, s7_make_function(s7, "[acc-" S_default_output_chans "]", acc_default_output_chans, 2, 0, false, "accessor"));
3957   s7_set_setter(s7, ss->default_output_srate_symbol, s7_make_function(s7, "[acc-" S_default_output_srate "]", acc_default_output_srate, 2, 0, false, "accessor"));
3958   s7_set_setter(s7, ss->ask_before_overwrite_symbol, s7_make_function(s7, "[acc-" S_ask_before_overwrite "]", acc_ask_before_overwrite, 2, 0, false, "accessor"));
3959   s7_set_setter(s7, ss->ask_about_unsaved_edits_symbol, s7_make_function(s7, "[acc-" S_ask_about_unsaved_edits "]", acc_ask_about_unsaved_edits, 2, 0, false, "accessor"));
3960   s7_set_setter(s7, ss->show_full_duration_symbol, s7_make_function(s7, "[acc-" S_show_full_duration "]", acc_show_full_duration, 2, 0, false, "accessor"));
3961   s7_set_setter(s7, ss->show_full_range_symbol, s7_make_function(s7, "[acc-" S_show_full_range "]", acc_show_full_range, 2, 0, false, "accessor"));
3962   s7_set_setter(s7, ss->remember_sound_state_symbol, s7_make_function(s7, "[acc-" S_remember_sound_state "]", acc_remember_sound_state, 2, 0, false, "accessor"));
3963   s7_set_setter(s7, ss->save_as_dialog_src_symbol, s7_make_function(s7, "[acc-" S_save_as_dialog_src "]", acc_save_as_dialog_src, 2, 0, false, "accessor"));
3964   s7_set_setter(s7, ss->save_as_dialog_auto_comment_symbol, s7_make_function(s7, "[acc-" S_save_as_dialog_auto_comment "]", acc_save_as_dialog_auto_comment, 2, 0, false, "accessor"));
3965   s7_set_setter(s7, ss->with_toolbar_symbol, s7_make_function(s7, "[acc-" S_with_toolbar "]", acc_with_toolbar, 2, 0, false, "accessor"));
3966   s7_set_setter(s7, ss->with_tooltips_symbol, s7_make_function(s7, "[acc-" S_with_tooltips "]", acc_with_tooltips, 2, 0, false, "accessor"));
3967   s7_set_setter(s7, ss->with_menu_icons_symbol, s7_make_function(s7, "[acc-" S_with_menu_icons "]", acc_with_menu_icons, 2, 0, false, "accessor"));
3968   s7_set_setter(s7, ss->initial_beg_symbol, s7_make_function(s7, "[acc-" S_initial_beg "]", acc_initial_beg, 2, 0, false, "accessor"));
3969   s7_set_setter(s7, ss->initial_dur_symbol, s7_make_function(s7, "[acc-" S_initial_dur "]", acc_initial_dur, 2, 0, false, "accessor"));
3970   s7_set_setter(s7, ss->auto_update_symbol, s7_make_function(s7, "[acc-" S_auto_update "]", acc_auto_update, 2, 0, false, "accessor"));
3971   s7_set_setter(s7, ss->auto_update_interval_symbol, s7_make_function(s7, "[acc-" S_auto_update_interval "]", acc_auto_update_interval, 2, 0, false, "accessor"));
3972   s7_set_setter(s7, ss->clipping_symbol, s7_make_function(s7, "[acc-" S_clipping "]", acc_clipping, 2, 0, false, "accessor"));
3973 #endif
3974 }
3975 
3976