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