1 #include "snd.h"
2 #include "sndlib-strings.h"
3 #include "clm2xen.h"
4 
5 
6 #define REGION_FILE 1
7 #define REGION_DEFERRED 0
8 
9 /* region data can be stored either in a temp file that is deleted when the region is deleted (hence must be copied upon insert or mix)
10  *    or as a descriptor of current chan/beg/num/edpos locs.  The descriptor form is used until some use is made of the data
11  *    that requires a file anyway (e.g. mixing), or if the data the descriptor depends on is about to be flushed (e.g. the
12  *    underlying edit list is about to be cleared, or the file is being closed, etc).
13  */
14 
15 
16 #define CLEAR_REGION_DATA 0
17 #define COMPLETE_DELETION 1
18 
19 static int region_id_ctr = 0;
20 
21 typedef struct {
22   int chans;
23   mus_long_t len;
24   chan_info **cps;
25   int *edpos;
26 } deferred_region;
27 
28 
free_deferred_region(deferred_region * dr)29 static deferred_region *free_deferred_region(deferred_region *dr)
30 {
31   if (dr)
32     {
33       if (dr->cps) free(dr->cps);
34       if (dr->edpos) free(dr->edpos);
35       free(dr);
36     }
37   return(NULL);
38 }
39 
40 
41 typedef struct region {
42   int chans;
43   mus_long_t framples;
44   int srate;                /* for file save (i.e. region->file) */
45   int header_type;          /* for file save */
46   snd_info *rsp;
47   char *name, *start, *end; /* for region browser */
48   char *filename;           /* if region data is stored in a temp file */
49   int use_temp_file;        /* REGION_FILE = in temp file 'filename', REGION_DEFERRED = in 'dr' */
50   mus_float_t maxamp;
51   mus_long_t maxamp_position;
52   snd_info *editor_copy;
53   char *editor_name;
54   int id;
55   deferred_region *dr;      /* REGION_DEFERRED descriptor */
56   peak_env_info **peak_envs;
57   mus_long_t *begs, *ends;
58 } region;
59 
60 
61 static void deferred_region_to_temp_file(region *r);
62 
free_region(region * r,int complete)63 static void free_region(region *r, int complete)
64 {
65   /* if not complete, just clear out old data (edited region being saved) */
66   if (r)
67     {
68       if (complete == COMPLETE_DELETION)
69 	{
70 	  if (r->editor_copy)
71 	    {
72 	      snd_info *sp;
73 	      sp = r->editor_copy;
74 	      sp->edited_region = NULL;
75 	      r->editor_copy = NULL;
76 	    }
77 	  if (r->name) free(r->name);
78 	  if (r->start) free(r->start);
79 	  if (r->end) free(r->end);
80 	  if (r->begs) free(r->begs);
81 	  if (r->ends) free(r->ends);
82 	  if (r->peak_envs)
83 	    {
84 	      int i;
85 	      for (i = 0; i < r->chans; i++)
86 		if (r->peak_envs[i])
87 		  r->peak_envs[i] = free_peak_env_info(r->peak_envs[i]);
88 	      free(r->peak_envs);
89 	      r->peak_envs = NULL;
90 	    }
91 	}
92       if (r->use_temp_file == REGION_FILE) /* we can delete this temp file because all references copy first */
93 	{
94 	  if (r->filename)
95 	    {
96 	      snd_remove(r->filename, REMOVE_FROM_CACHE);
97 	      free(r->filename);
98 	    }
99 	  r->filename = NULL;
100 	}
101       if (r->use_temp_file == REGION_DEFERRED)
102 	r->dr = free_deferred_region(r->dr);
103       if (r->rsp)
104 	{
105 	  if ((r->rsp->chans) &&
106 	      (r->rsp->chans[0]->edits))
107 	    r->rsp->chans[0]->edits[0]->peak_env = NULL;
108 	  r->rsp = completely_free_snd_info(r->rsp);
109 	}
110       if (complete == COMPLETE_DELETION) free(r);
111     }
112 }
113 
114 
115 static region **regions = NULL;
116 static int regions_size = 0, regions_allocated_size = 0;
117 
allocate_regions(int numreg)118 void allocate_regions(int numreg)
119 {
120   int i;
121   if (numreg > regions_allocated_size)
122     {
123       if (regions)
124 	{
125 	  regions = (region **)realloc(regions, numreg * sizeof(region *));
126 	  for (i = regions_allocated_size; i < numreg; i++) regions[i] = NULL;
127 	}
128       else regions = (region **)calloc(numreg, sizeof(region *));
129       regions_allocated_size = numreg;
130     }
131   if (regions_size > numreg)
132     {
133       for (i = numreg; i < regions_size; i++)
134 	if (regions[i])
135 	  {
136 	    free_region(regions[i], COMPLETE_DELETION);
137 	    regions[i] = NULL;
138 	  }
139       if (region_browser_is_active()) update_region_browser(true);
140     }
141   regions_size = numreg;
142 }
143 
144 
set_max_regions(int n)145 static void set_max_regions(int n)
146 {
147   if (n >= 0)
148     {
149       allocate_regions(n);
150       allocate_region_rows(n);
151       in_set_max_regions(n);
152     }
153 }
154 
155 
region_id_to_list_position(int id)156 int region_id_to_list_position(int id)
157 {
158   int i;
159   if ((id >= 0) && (id < region_id_ctr))
160     for (i = 0; i < regions_size; i++)
161       if ((regions[i]) &&
162 	  (regions[i]->id == id))
163 	return(i);
164   return(INVALID_REGION);
165 }
166 
167 
region_list_position_to_id(int n)168 int region_list_position_to_id(int n)
169 {
170   if ((n >= 0) &&
171       (n < regions_size) &&
172       (regions[n]))
173     return(regions[n]->id);
174   return(INVALID_REGION);
175 }
176 
177 
id_to_region(int id)178 static region *id_to_region(int id)
179 {
180   int i;
181   if ((id >= 0) && (id < region_id_ctr))
182     for (i = 0; i < regions_size; i++)
183       if ((regions[i]) &&
184 	  (regions[i]->id == id))
185 	return(regions[i]);
186   return(NULL);
187 }
188 
189 
region_ok(int id)190 bool region_ok(int id)
191 {
192   return((bool)id_to_region(id));
193 }
194 
195 
region_len(int n)196 mus_long_t region_len(int n)
197 {
198   region *r;
199   r = id_to_region(n);
200   if (r)
201     return(r->framples);
202   return(0);
203 }
204 
205 
region_chans(int n)206 int region_chans(int n)
207 {
208   region *r;
209   r = id_to_region(n);
210   if (r)
211     return(r->chans);
212   return(0);
213 }
214 
215 
region_srate(int n)216 int region_srate(int n)
217 {
218   region *r;
219   r = id_to_region(n);
220   if (r)
221     return(r->srate);
222   return(0);
223 }
224 
225 
region_file_name(int n)226 const char *region_file_name(int n)
227 {
228   region *r;
229   r = id_to_region(n);
230   if (r)
231     return(r->name);
232   return(NULL);
233 }
234 
235 
get_region_maxamp(region * r)236 static void get_region_maxamp(region *r)
237 {
238   /* it exists as r->filename, so just use sndlib... */
239   mus_float_t *vals;
240   mus_long_t *times;
241   int i;
242   mus_long_t maxpos;
243   mus_float_t maxsamp;
244   vals = (mus_float_t *)calloc(r->chans, sizeof(mus_float_t));
245   times = (mus_long_t *)calloc(r->chans, sizeof(mus_long_t));
246   mus_sound_maxamps(r->filename, r->chans, vals, times);
247   maxpos = times[0];
248   maxsamp = vals[0];
249   for (i = 1; i < r->chans; i++)
250     if (vals[i] > maxsamp)
251       {
252 	maxsamp = vals[i];
253 	maxpos = times[i];
254       }
255   free(vals);
256   free(times);
257   r->maxamp = maxsamp;
258   r->maxamp_position = maxpos;
259 }
260 
261 
region_maxamp(int n)262 mus_float_t region_maxamp(int n)
263 {
264   region *r;
265   r = id_to_region(n);
266   if (r)
267     {
268       if (r->maxamp < 0.0)
269 	{
270 	  if (r->use_temp_file == REGION_DEFERRED)
271 	    deferred_region_to_temp_file(r);
272 	  get_region_maxamp(r);
273 	}
274       return(r->maxamp);
275     }
276   return(0.0);
277 }
278 
279 
region_maxamp_position(int n)280 static mus_long_t region_maxamp_position(int n)
281 {
282   region *r;
283   r = id_to_region(n);
284   if (r)
285     {
286       if (r->maxamp < 0.0)
287 	{
288 	  if (r->use_temp_file == REGION_DEFERRED)
289 	    deferred_region_to_temp_file(r);
290 	  get_region_maxamp(r);
291 	}
292       return(r->maxamp_position);
293     }
294   return(-1);
295 }
296 
297 
region_sample(int reg,int chn,mus_long_t samp)298 static mus_float_t region_sample(int reg, int chn, mus_long_t samp)
299 {
300   region *r;
301   r = id_to_region(reg);
302   if (r)
303     {
304       if ((samp < r->framples) && (chn < r->chans))
305 	{
306 	  snd_fd *sf;
307 	  mus_float_t val;
308 	  deferred_region *drp;
309 	  switch (r->use_temp_file)
310 	    {
311 	    case REGION_FILE:
312 	      sf = init_region_read(samp, reg, chn, READ_FORWARD);
313 	      val = read_sample(sf);
314 	      free_snd_fd(sf);
315 	      return(val);
316 
317 	    case REGION_DEFERRED:
318 	      drp = r->dr;
319 	      return(chn_sample(samp + r->begs[chn], drp->cps[chn], drp->edpos[chn]));
320 	    }
321 	}
322     }
323   return(0.0);
324 }
325 
326 
region_current_location(snd_fd * fd)327 mus_long_t region_current_location(snd_fd *fd)
328 {
329   region *r;
330   r = id_to_region(fd->region);
331   switch (r->use_temp_file)
332     {
333     case REGION_FILE:
334       return(current_location(fd));
335     case REGION_DEFERRED:
336       return(current_location(fd) - r->begs[0]);
337     }
338   return(-1);
339 }
340 
341 
region_samples(int reg,int chn,mus_long_t beg,mus_long_t num,mus_float_t * data)342 static void region_samples(int reg, int chn, mus_long_t beg, mus_long_t num, mus_float_t *data)
343 {
344   region *r;
345   r = id_to_region(reg);
346   if (r)
347     {
348       if ((beg < r->framples) && (chn < r->chans))
349 	{
350 	  snd_fd *sf;
351 	  deferred_region *drp;
352 	  switch (r->use_temp_file)
353 	    {
354 	    case REGION_FILE:
355 	      sf = init_region_read(beg, reg, chn, READ_FORWARD);
356 	      samples_to_vct_with_reader(num, data, sf);
357 	      free_snd_fd(sf);
358 	      break;
359 
360 	    case REGION_DEFERRED:
361 	      drp = r->dr;
362 	      sf = init_sample_read_any_with_bufsize(beg + r->begs[chn], drp->cps[chn], READ_FORWARD, drp->edpos[chn], num);
363 	      samples_to_vct_with_reader(num, data, sf);
364 	      free_snd_fd(sf);
365 	      break;
366 	    }
367 	}
368     }
369 }
370 
371 
first_region_active(void)372 static int first_region_active(void)
373 {
374   int i;
375   for (i = 0; i < regions_size; i++)
376     if (regions[i])
377       return(i);
378   return(NO_REGIONS);
379 }
380 
381 
check_regions(void)382 static int check_regions(void)
383 {
384   int act;
385   act = first_region_active();
386   if (act == NO_REGIONS)
387     reflect_no_regions_in_region_browser();
388   return(act);
389 }
390 
391 
make_region_readable(region * r)392 static void make_region_readable(region *r)
393 {
394   snd_info *regsp;
395   file_info *hdr;
396   int i;
397 
398   if (r->use_temp_file == REGION_DEFERRED)
399     deferred_region_to_temp_file(r);
400   if (r->rsp) return;
401 
402   regsp = make_basic_snd_info(r->chans);
403   regsp->nchans = r->chans;
404   regsp->hdr = (file_info *)calloc(1, sizeof(file_info));
405   regsp->inuse = SOUND_READER;
406 
407   hdr = regsp->hdr;
408   hdr->samples = r->framples * r->chans;
409   hdr->srate = r->srate;
410   hdr->chans = r->chans;
411   hdr->comment = NULL;
412 
413   for (i = 0; i < r->chans; i++)
414     {
415       hdr = make_file_info(r->filename, FILE_READ_ONLY, FILE_NOT_SELECTED);
416       if (hdr)
417 	{
418 	  snd_io *io;
419 	  int fd;
420 	  chan_info *cp;
421 
422 	  cp = make_chan_info(NULL, i, regsp);
423 	  cp->editable = false;
424 	  regsp->chans[i] = cp;
425 	  add_channel_data_1(cp, r->srate, r->framples, WITHOUT_GRAPH);
426 	  cp->hookable = WITHOUT_HOOK;
427 
428 	  fd = snd_open_read(r->filename);
429 	  snd_file_open_descriptors(fd,
430 				    r->filename,
431 				    hdr->sample_type,
432 				    hdr->data_location,
433 				    hdr->chans,
434 				    hdr->type);
435 	  io = make_file_state(fd, hdr, i, 0, FILE_BUFFER_SIZE);
436 	  cp->sounds[0] = make_snd_data_file(r->filename, io, hdr, DONT_DELETE_ME, cp->edit_ctr, i); /* don't auto-delete! */
437 	}
438       else
439 	{
440 	  Xen_error(Xen_make_error_type("IO-error"),
441 		    Xen_list_3(C_string_to_Xen_string("can't read region file ~S, ~A"),
442 			       C_string_to_Xen_string(r->filename),
443 			       C_string_to_Xen_string(snd_open_strerror())));
444 	}
445     }
446   r->rsp = regsp;
447 }
448 
449 
fixup_region_data(chan_info * cp,int chan,int pos)450 file_info *fixup_region_data(chan_info *cp, int chan, int pos)
451 {
452   /* for region browser labels */
453   if ((pos >= 0) &&
454       (pos < regions_size) &&
455       (regions[pos]))
456     {
457       region *r;
458       r = regions[pos];
459       if (chan < r->chans)
460 	{
461 	  snd_info *nsp;
462 	  chan_info *ncp;
463 
464 	  make_region_readable(r);
465 	  nsp = r->rsp;
466 	  ncp = nsp->chans[chan];
467 
468 	  cp->sounds = ncp->sounds;
469 	  cp->sound_size = ncp->sound_size;
470 	  cp->edits = ncp->edits; /* ?? is this safe ?? */
471 	  cp->edit_size = ncp->edit_size;
472 	  cp->edit_ctr = ncp->edit_ctr;
473 	  cp->edits[0]->samples = ncp->edits[0]->samples;
474 	  cp->axis = ncp->axis;
475 
476 	  /* cp here is actually region sound chan 0 */
477 	  if ((r->peak_envs) && (r->peak_envs[chan]))
478 	    cp->edits[0]->peak_env = r->peak_envs[chan];
479 	  else
480 	    {
481 	      if (cp->edits[0]->peak_env)
482 		cp->edits[0]->peak_env = NULL;
483 	    }
484 
485 	  initialize_scrollbars(cp);
486 	  return(nsp->hdr);
487 	}
488     }
489   return(NULL);
490 }
491 
492 
for_each_region_chan_with_refint(void (* func)(chan_info * ncp,int * val),int * value)493 void for_each_region_chan_with_refint(void (*func)(chan_info *ncp, int *val), int *value)
494 {
495   /* used only in snd-io.c to remove dangling temp files (probably can't actually happen) */
496   int i;
497   for (i = 0; i < regions_size; i++)
498     {
499       int chn;
500       region *r;
501       r = regions[i];
502       if ((r) && (r->rsp) && (r->use_temp_file == REGION_FILE))
503 	for (chn = 0; chn < r->chans; chn++)
504 	  {
505 	    chan_info *cp;
506 	    cp = r->rsp->chans[chn];
507 	    (*func)(cp, value);
508 	  }
509     }
510 }
511 
512 
region_report(void)513 region_state *region_report(void)
514 {
515   region_state *rs;
516   int i, len, size;
517 
518   rs = (region_state *)calloc(1, sizeof(region_state));
519   len = regions_size;
520   for (i = 0; i < regions_size; i++)
521     if (!(regions[i]))
522       {
523 	len = i;
524 	break;
525       }
526   rs->len = len;
527   if (len == 0) return(rs);
528   size = len * sizeof(char *);
529   rs->name = (char **)calloc(size, 1);
530   for (i = 0; i < len; i++)
531     {
532       region *r;
533       char *reg_buf;
534       r = regions[i];
535       reg_buf = (char *)calloc(LABEL_BUFFER_SIZE, sizeof(char));
536       snprintf(reg_buf, LABEL_BUFFER_SIZE, "%d: %s (%s:%s)", r->id, r->name, r->start, r->end);
537       rs->name[i] = reg_buf;
538     }
539   return(rs);
540 }
541 
542 
region_description(int rg)543 char *region_description(int rg)
544 {
545   region *r;
546   r = id_to_region(rg);
547   if (r)
548     return(mus_format("region data from %s (%s : %s)", r->name, r->start, r->end));
549   return(NULL);
550 }
551 
552 
free_region_state(region_state * r)553 void free_region_state(region_state *r)
554 {
555   if (r)
556     {
557       int i;
558       for (i = 0; i < r->len; i++)
559 	if (r->name[i])
560 	  free(r->name[i]);
561       if (r->name) free(r->name);
562       free(r);
563     }
564 }
565 
566 
remove_region_from_list(int pos)567 int remove_region_from_list(int pos) /* region browser */
568 {
569   int i, id;
570   id = region_list_position_to_id(pos);
571   if (id == INVALID_REGION) return(INVALID_REGION);
572 
573   stop_playing_region(id, PLAY_CLOSE);
574 
575   free_region(id_to_region(id), COMPLETE_DELETION);
576   for (i = pos; i < regions_size - 1; i++)
577     regions[i] = regions[i + 1];
578   regions[regions_size - 1] = NULL;
579 
580   return(check_regions());
581 }
582 
583 
add_to_region_list(region * r)584 static void add_to_region_list(region *r)
585 {
586   int i, okr = -1;
587   for (i = max_regions(ss) - 1; i >= 0; i--)
588     {
589       if (!(regions[i]))
590 	{
591 	  okr = i;
592 	  break;
593 	}
594     }
595   if (okr == -1)
596     okr = max_regions(ss) - 1;
597   if (regions[okr])
598     {
599       stop_playing_region(regions[okr]->id, PLAY_CLOSE);
600       free_region(regions[okr], COMPLETE_DELETION);
601     }
602   for (i = okr; i > 0; i--)
603     regions[i] = regions[i - 1];
604   regions[0] = r;
605   if (!r) check_regions();
606 }
607 
608 
609 #define NOT_EDITABLE -2
610 
paste_region_1(int n,chan_info * cp,bool add,mus_long_t beg,io_error_t * err,int start_chan,int * out_chans)611 static int paste_region_1(int n, chan_info *cp, bool add, mus_long_t beg, io_error_t *err, int start_chan, int *out_chans)
612 {
613   region *r;
614   char *origin = NULL;
615   int id = -1;
616   io_error_t io_err;
617   sync_info *si = NULL;
618 
619   r = id_to_region(n);
620   if ((!r) ||
621       (r->framples == 0))
622     return(INVALID_REGION);
623 
624   if (!(is_editable(cp)))
625     {
626       (*err) = IO_EDIT_HOOK_CANCELLATION;
627       return(NOT_EDITABLE);
628     }
629 
630   if (r->use_temp_file == REGION_DEFERRED)
631     deferred_region_to_temp_file(r);
632 
633   si = sync_to_chan(cp);
634   (*out_chans) = si->chans;
635 
636   if (add)
637     {
638       /* unfortunately we need to copy here since the region may fall off the region stack while we're still using the mix */
639       char *newname;
640       newname = shorter_tempnam(temp_dir(ss), "snd_");
641       io_err = copy_file(r->filename, newname);
642       if (io_err != IO_NO_ERROR)
643 	{
644 	  (*err) = io_err;
645 	  return(INVALID_REGION);
646 	}
647       else
648 	{
649 #if HAVE_FORTH
650 	  origin = mus_format("%d %s %" print_mus_long " %s drop", n, S_integer_to_region, beg, S_mix_region);
651 #endif
652 #if HAVE_RUBY
653 	  origin = mus_format("%s(%s(%d), %" print_mus_long, to_proc_name(S_mix_region), to_proc_name(S_integer_to_region), n, beg);
654 #endif
655 #if HAVE_SCHEME || (!HAVE_EXTENSION_LANGUAGE)
656 	  origin = mus_format("%s (%s %d) %" print_mus_long, S_mix_region, S_integer_to_region, n, beg);
657 #endif
658 	  if (si->chans > 1)
659 	    remember_temp(newname, si->chans);
660 
661 	  id = mix_file(beg, r->framples, si->chans, si->cps, newname,
662 			(si->chans > 1) ? MULTICHANNEL_DELETION : DELETE_ME,
663 			origin, with_mix_tags(ss), start_chan);
664 	  free(origin);
665 	}
666       if (newname) free(newname);
667     }
668   else
669     {
670       int i;
671       char *tempfile = NULL;
672       if (r->use_temp_file == REGION_FILE)
673 	{
674 	  tempfile = snd_tempnam();
675 	  io_err = copy_file(r->filename, tempfile);
676 	  if (io_err != IO_NO_ERROR)
677 	    {
678 	      if (si) free_sync_info(si);
679 	      (*err) = io_err;
680 	      return(INVALID_REGION);
681 	    }
682 	  else
683 	    if (r->chans > 1)
684 	      remember_temp(tempfile, r->chans);
685 	}
686 
687 #if HAVE_FORTH
688 	  origin = mus_format("%d %s %" print_mus_long " %s drop", n, S_integer_to_region, beg, S_insert_region);
689 #endif
690 #if HAVE_RUBY
691 	  origin = mus_format("%s(%s(%d), %" print_mus_long, to_proc_name(S_insert_region), to_proc_name(S_integer_to_region), n, beg);
692 #endif
693 #if HAVE_SCHEME || (!HAVE_EXTENSION_LANGUAGE)
694 	  origin = mus_format("%s (%s %d) %" print_mus_long, S_insert_region, S_integer_to_region, n, beg);
695 #endif
696 
697       for (i = 0; ((i < r->chans) && (i < si->chans)); i++)
698 	{
699 	  chan_info *ncp;
700 	  ncp = si->cps[i];                       /* currently syncd chan that we might paste to */
701 	  if (file_insert_samples(beg, r->framples, tempfile, ncp, i,
702 				  (r->chans > 1) ? MULTICHANNEL_DELETION : DELETE_ME,
703 				  origin, ncp->edit_ctr))
704 	    update_graph(si->cps[i]);
705 	}
706       free(origin);
707       if ((r->use_temp_file == REGION_FILE) && (tempfile)) free(tempfile);
708     }
709   if (si) free_sync_info(si);
710   return(id);
711 }
712 
713 
paste_region_2(int n,chan_info * cp,bool add,mus_long_t beg)714 static io_error_t paste_region_2(int n, chan_info *cp, bool add, mus_long_t beg)
715 {
716   io_error_t err = IO_NO_ERROR;
717   int chans = 0;
718   paste_region_1(n, cp, add, beg, &err, 0, &chans);
719   return(err);
720 }
721 
722 
paste_region(int n,chan_info * cp)723 io_error_t paste_region(int n, chan_info *cp)
724 {
725   return(paste_region_2(n, cp, false, cursor_sample(cp)));
726 }
727 
728 
add_region(int n,chan_info * cp)729 io_error_t add_region(int n, chan_info *cp)
730 {
731   return(paste_region_2(n, cp, true, cursor_sample(cp)));
732 }
733 
734 
define_region(sync_info * si,mus_long_t * ends)735 int define_region(sync_info *si, mus_long_t *ends)
736 {
737   /* now look at all sync'd channels, collect them into the current region */
738   /* we created the necessary pointers in create_selection above */
739   int i;
740   mus_long_t len;
741   chan_info *cp0;
742   snd_info *sp0;
743   region *r;
744   deferred_region *drp;
745 
746   len = 0;
747   for (i = 0; i < si->chans; i++)
748     if (len < (ends[i] - si->begs[i]))
749       len = ends[i] - si->begs[i];
750   len += 1;
751   if (len <= 0) return(INVALID_REGION);
752 
753   cp0 = si->cps[0];
754   sp0 = cp0->sound;
755 
756   r = (region *)calloc(1, sizeof(region));
757   r->id = region_id_ctr++;
758 
759   if (regions[0])
760     add_to_region_list(r);
761   else regions[0] = r;
762 
763   r->header_type = (sp0->hdr)->type;
764   r->srate = snd_srate(sp0);
765   r->maxamp = -1.0;
766   r->maxamp_position = -1;
767   r->editor_copy = NULL;
768   r->name = mus_strdup(sp0->short_filename);
769   r->chans = si->chans;
770   r->framples = len;
771   r->start = prettyf((double)(si->begs[0]) / (double)(r->srate), 2);
772   r->end = prettyf((double)(ends[0]) / (double)(r->srate), 2);
773   r->use_temp_file = REGION_DEFERRED;
774   r->begs = (mus_long_t *)calloc(r->chans, sizeof(mus_long_t));
775   r->ends = (mus_long_t *)calloc(r->chans, sizeof(mus_long_t));
776 
777   ss->deferred_regions++;
778   r->dr = (deferred_region *)calloc(1, sizeof(deferred_region));
779   drp = r->dr;
780   drp->chans = si->chans;
781   drp->cps = (chan_info **)calloc(drp->chans, sizeof(chan_info *));
782   drp->edpos = (int *)calloc(drp->chans, sizeof(int));
783   drp->len = len;
784 
785   for (i = 0; i < drp->chans; i++)
786     {
787       drp->cps[i] = si->cps[i];
788       r->begs[i] = si->begs[i];
789       r->ends[i] = ends[i] - si->begs[i];
790       drp->edpos[i] = drp->cps[i]->edit_ctr;
791       if (r->ends[i] > PEAK_ENV_CUTOFF)
792 	{
793 	  peak_env_info *ep;
794 	  ep = drp->cps[i]->edits[drp->edpos[i]]->peak_env;
795 	  if ((ep) && (ep->completed))
796 	    {
797 	      if (!r->peak_envs)
798 		r->peak_envs = (peak_env_info **)calloc(r->chans, sizeof(peak_env_info *));
799 	      r->peak_envs[i] = peak_env_section(drp->cps[i], r->begs[i], r->ends[i] + 1, drp->edpos[i]);
800 	    }
801 	}
802     }
803 
804   reflect_regions_in_region_browser();
805   if (region_browser_is_active()) update_region_browser(true);
806   return(r->id);
807 }
808 
809 
deferred_region_to_temp_file(region * r)810 static void deferred_region_to_temp_file(region *r)
811 {
812   int i, datumb = 0;
813   bool copy_ok;
814   mus_long_t alloc_len, len = 0;
815   snd_fd **sfs = NULL;
816   snd_info *sp0;
817   deferred_region *drp = NULL;
818   mus_float_t **data = NULL;
819 
820   ss->deferred_regions--;
821   drp = r->dr;
822   len = drp->len;
823   r->use_temp_file = REGION_FILE;
824   r->filename = snd_tempnam();
825   sp0 = drp->cps[0]->sound;
826 
827   copy_ok = ((mus_header_writable(MUS_NEXT, sp0->hdr->sample_type)) &&
828 	     (r->chans == (int)sp0->nchans) &&
829 	     (r->peak_envs) &&
830 	     ((drp->len - 1) == r->ends[0]));
831   if (copy_ok)
832     for (i = 0; i < r->chans; i++)
833       if ((drp->edpos[i] != 0) ||
834 	  (drp->cps[i]->sound != sp0) ||
835 	  (r->begs[i] != r->begs[0]) ||
836 	  (r->ends[i] != (drp->len - 1)) ||
837 	  (!r->peak_envs[i]))
838 	{
839 	  copy_ok = false;
840 	  break;
841 	}
842 
843   if (copy_ok)
844     {
845       /* write next header with correct len
846        * seek loc in sp0->filename (r->begs[0])
847        * copy len*data-size bytes
848        * get max from amp envs
849        */
850       mus_long_t err;
851 
852       datumb = mus_bytes_per_sample(sp0->hdr->sample_type);
853       err = mus_write_header(r->filename, MUS_NEXT, r->srate, r->chans, drp->len * r->chans, sp0->hdr->sample_type, "region deferred temp");
854 
855       if (err != MUS_NO_ERROR)
856 	snd_error("can't write region temp file %s: %s", r->filename, snd_io_strerror());
857       else
858 	{
859 	  int fdi, fdo;
860 	  mus_long_t oloc;
861 	  oloc = mus_header_data_location();
862 	  fdo = snd_reopen_write(r->filename);
863 	  lseek(fdo, oloc, SEEK_SET);
864 	  fdi = mus_file_open_read(sp0->filename);
865 	  if (fdi == -1)
866 	    snd_error("can't read region's original sound? %s: %s", sp0->filename, snd_io_strerror());
867 	  else
868 	    {
869 	      mus_long_t j, data_size;
870 	      char *buffer;
871 
872 	      lseek(fdi, sp0->hdr->data_location + r->chans * datumb * r->begs[0], SEEK_SET);
873 	      data_size = drp->len * r->chans * datumb;
874 	      if (data_size > REPORTING_SIZE)
875 		alloc_len = REPORTING_SIZE;
876 	      else alloc_len = data_size;
877 	      buffer = (char *)malloc(alloc_len * sizeof(char));
878 	      for (j = 0; j < data_size; j += alloc_len)
879 		{
880 		  mus_long_t bytes;
881 		  ssize_t n;
882 		  bytes = data_size - j;
883 		  if (bytes > alloc_len)
884 		    bytes = alloc_len;
885 
886 		  /* read and write return 0 to indicate end of file, apparently */
887 		  n = read(fdi, buffer, bytes);
888 
889 		  if (n < 0)
890 		    fprintf(stderr, "IO error while reading region temp file: %d %s\n", (int)n, strerror(errno));
891 		  if (n > 0)
892 		    {
893 		      n = write(fdo, buffer, bytes);
894 		      if (n < 0)
895 			fprintf(stderr, "IO error while writing region temp file: %d %s\n", (int)n, strerror(errno));
896 		    }
897 		}
898 	      free(buffer);
899 	      snd_close(fdi, sp0->filename);
900 	    }
901 	  snd_close(fdo, r->filename);
902 	}
903     }
904   else
905     {
906       io_error_t io_err = IO_NO_ERROR;
907       file_info *hdr = NULL;
908       int ofd;
909 
910       hdr = make_temp_header(r->filename, r->srate, r->chans, 0, (char *)__func__);
911       ofd = open_temp_file(r->filename, r->chans, hdr, &io_err);
912       if (ofd == -1)
913 	snd_error("%s region temp file %s: %s",
914 		  (io_err != IO_NO_ERROR) ? io_error_name(io_err) : "can't open",
915 		  r->filename,
916 		  snd_open_strerror());
917       else
918 	{
919 	  sfs = (snd_fd **)calloc(r->chans, sizeof(snd_fd *));
920 	  data = (mus_float_t **)calloc(r->chans, sizeof(mus_float_t *));
921 	  datumb = mus_bytes_per_sample(hdr->sample_type);
922 	  /* here if peak_envs, maxamp exists */
923 
924 	  alloc_len = len;
925 	  if (alloc_len > REPORTING_SIZE)
926 	    alloc_len = REPORTING_SIZE;
927 
928 	  for (i = 0; i < r->chans; i++)
929 	    {
930 	      sfs[i] = init_sample_read_any(r->begs[i], drp->cps[i], READ_FORWARD, drp->edpos[i]);
931 	      data[i] = (mus_float_t *)calloc(alloc_len, sizeof(mus_float_t));
932 	    }
933 
934 	  if ((r->chans == 1) &&
935 	      (r->ends[0] == (len - 1)))
936 	    {
937 	      snd_fd *sf;
938 	      mus_float_t *d;
939 
940 	      sf = sfs[0];
941 	      d = data[0];
942 	      if (len <= REPORTING_SIZE)
943 		{
944 		  samples_to_vct_with_reader(len, d, sf);
945 		  mus_file_write(ofd, 0, len - 1, 1, data);
946 		}
947 	      else
948 		{
949 		  int k;
950 		  sampler_set_safe(sf, len);
951 		  for (k = 0; k < len; k += alloc_len)
952 		    {
953 		      mus_long_t kdur, n;
954 		      int err;
955 		      kdur = len - k;
956 		      if (kdur > alloc_len) kdur = alloc_len;
957 		      for (n = 0; n < kdur; n++)
958 			d[n] = read_sample(sf);
959 		      err = mus_file_write(ofd, 0, kdur - 1, 1, data);
960 		      if (err != MUS_NO_ERROR) break;
961 		    }
962 		}
963 	    }
964 	  else
965 	    {
966 	      if (len <= REPORTING_SIZE)
967 		{
968 		  for (i = 0; i < r->chans; i++)
969 		    samples_to_vct_with_reader(len, data[i], sfs[i]);
970 		  mus_file_write(ofd, 0, len - 1, r->chans, data);
971 		}
972 	      else
973 		{
974 		  int k;
975 		  for (i = 0; i < r->chans; i++)
976 		    sampler_set_safe(sfs[i], len);
977 		  for (k = 0; k < len; k += alloc_len)
978 		    {
979 		      int err;
980 		      mus_long_t kdur;
981 
982 		      kdur = len - k;
983 		      if (kdur > alloc_len) kdur = alloc_len;
984 		      for (i = 0; i < r->chans; i++)
985 			{
986 			  mus_long_t n;
987 			  snd_fd *p;
988 			  mus_float_t *buf;
989 			  buf = data[i];
990 			  p = sfs[i];
991 			  for (n = 0; n < kdur; n++)
992 			    buf[n] = read_sample(p);
993 			}
994 		      err = mus_file_write(ofd, 0, kdur - 1, r->chans, data);
995 		      if (err != MUS_NO_ERROR) break;
996 		    }
997 		}
998 	    }
999 
1000 	  close_temp_file(r->filename, ofd, hdr->type, len * r->chans * datumb);
1001 	  for (i = 0; i < r->chans; i++) free(data[i]);
1002 	  for (i = 0; i < r->chans; i++) free_snd_fd(sfs[i]);
1003 	  free(sfs);
1004 	  free(data);
1005 	  data = NULL;
1006 	}
1007       free_file_info(hdr);
1008     }
1009   r->dr = free_deferred_region(r->dr);
1010 }
1011 
1012 
sequester_deferred_regions(chan_info * cp,int edit_top)1013 void sequester_deferred_regions(chan_info *cp, int edit_top)
1014 {
1015   region *r;
1016   deferred_region *drp;
1017   int i;
1018   for (i = 0; i < regions_size; i++)
1019     {
1020       r = regions[i];
1021       if ((r) && (r->use_temp_file == REGION_DEFERRED))
1022 	{
1023 	  int j;
1024 	  drp = r->dr;
1025 	  for (j = 0; j < drp->chans; j++)
1026 	    if ((drp->cps[j] == cp) &&
1027 		(drp->edpos[j] > edit_top))
1028 	      {
1029 		if (r->ends[j] > 1000000)
1030 		  status_report(cp->sound, "sequestering region %d...", r->id);
1031 		deferred_region_to_temp_file(r);
1032 		if (r->ends[j] > 1000000)
1033 		  clear_status_area(cp->sound);
1034 		break;
1035 	      }
1036 	}
1037     }
1038 }
1039 
1040 
init_region_read(mus_long_t beg,int n,int chan,read_direction_t direction)1041 snd_fd *init_region_read(mus_long_t beg, int n, int chan, read_direction_t direction)
1042 {
1043   /* conjure up a reasonable looking ed list and sound list */
1044   region *r;
1045   r = id_to_region(n);
1046   if ((r) && (chan < r->chans))
1047     {
1048       if ((beg == 0) &&
1049 	  (direction == READ_BACKWARD))
1050 	beg = r->framples - 1;
1051       if (r->use_temp_file == REGION_DEFERRED)
1052 	{
1053 	  deferred_region *drp;
1054 	  drp = r->dr;
1055 	  return(init_sample_read_any(r->begs[chan] + beg, drp->cps[chan], direction, drp->edpos[chan]));
1056 	}
1057       else
1058 	{
1059 	  make_region_readable(r);
1060 	  return(init_sample_read(beg, r->rsp->chans[chan], direction));
1061 	}
1062     }
1063   return(NULL);
1064 }
1065 
1066 
cleanup_region_temp_files(void)1067 void cleanup_region_temp_files(void)
1068 { /* called upon exit to get rid of lingering region-related temp files */
1069   int i;
1070   for (i = 0; i < regions_size; i++)
1071     {
1072       region *r;
1073       r = regions[i];
1074       if ((r) &&
1075 	  (r->use_temp_file == REGION_FILE) &&
1076 	  (r->filename))
1077 	{
1078 	  snd_remove(r->filename, REMOVE_FROM_CACHE);
1079 	  free(r->filename);
1080 	  r->filename = NULL;
1081 	}
1082     }
1083 }
1084 
1085 
snd_regions(void)1086 int snd_regions(void)
1087 {
1088   int i, num;
1089   num = 0;
1090   for (i = 0; i < regions_size; i++)
1091     if (regions[i])
1092       num++;
1093   return(num);
1094 }
1095 
1096 
1097 /* (restore-region n chans len srate maxamp name start end filename [date-and-length]) */
1098 
save_regions(FILE * fd)1099 void save_regions(FILE *fd)
1100 {
1101   int i;
1102   for (i = 0; i < regions_size; i++)
1103     {
1104       region *r;
1105       r = regions[i];
1106       if (r)
1107 	{
1108 	  io_error_t io_err;
1109 	  char *newname;
1110 	  char *ofile = NULL;
1111 
1112 	  if (r->use_temp_file == REGION_DEFERRED)
1113 	    deferred_region_to_temp_file(r);
1114 	  ofile = shorter_tempnam(save_dir(ss), "snd_save_");
1115 	  newname = run_save_state_hook(ofile);
1116 	  free(ofile);
1117 
1118 	  io_err = copy_file(r->filename, newname);
1119 	  if (io_err != IO_NO_ERROR)
1120 	    {
1121 	      snd_warning("trying to save region %d (%s) in %s: %s, %s",
1122 			  r->id, r->filename, newname, io_error_name(io_err),
1123 			  strerror(ss->local_errno));
1124 	    }
1125 	  else
1126 	    {
1127 #if HAVE_RUBY
1128 	      fprintf(fd, "%s(%d, %d, %" print_mus_long ", %d, %.4f, \"%s\", \"%s\", \"%s\", ",
1129 		      "restore_region", i, r->chans, r->framples, r->srate, r->maxamp, r->name, r->start, r->end);
1130 	      fprintf(fd, " \"%s\", [%d, %" print_mus_long "])\n",
1131 		      newname,
1132 		      (int)mus_sound_write_date(newname),
1133 		      mus_sound_length(newname));
1134 #endif
1135 #if HAVE_SCHEME
1136 	      fprintf(fd, "(%s %d %d %" print_mus_long " %d %.4f \"%s\" \"%s\" \"%s\"",
1137 		      S_restore_region, i, r->chans, r->framples, r->srate, r->maxamp, r->name, r->start, r->end);
1138 	      fprintf(fd, " \"%s\" (list %d %" print_mus_long "))\n",
1139 		      newname,
1140 		      (int)mus_sound_write_date(newname),
1141 		      mus_sound_length(newname));
1142 #endif
1143 #if HAVE_FORTH
1144 	  fprintf(fd, "%d %d %" print_mus_long " %d %.4f \"%s\" \"%s\" \"%s\"",
1145 	          i, r->chans, r->framples, r->srate, r->maxamp, r->name, r->start, r->end);
1146  	  fprintf(fd, " \"%s\" '( %d %" print_mus_long " ) %s drop\n",
1147  		  newname,
1148  		  (int)mus_sound_write_date(newname),
1149  		  mus_sound_length(newname),
1150 		  S_restore_region);
1151 #endif
1152 	    }
1153 	  free(newname);
1154 	}
1155     }
1156 }
1157 
1158 
region_edit(int pos)1159 void region_edit(int pos)
1160 {
1161   /* from region browser:
1162    *   load region into temp file, load that into snd editor,
1163    *   if 'save', save temp file and update region (browser also) (cancelling active display if any)
1164    *   while editing, if delete in browser, cut backpointer in editor and signal it
1165    */
1166   region *r = NULL;
1167   if ((pos >= 0) &&
1168       (pos < regions_size) &&
1169       (regions[pos]))
1170     r = regions[pos];
1171   if (r)
1172     {
1173       if (r->editor_copy)
1174 	snd_error("region %d already being edited", r->id);
1175       else
1176 	{
1177 	  io_error_t io_err;
1178 	  char *temp_region_name;
1179 	  if (r->use_temp_file == REGION_DEFERRED)
1180 	    deferred_region_to_temp_file(r);
1181 	  temp_region_name = shorter_tempnam(temp_dir(ss), "region-");
1182 	  io_err = copy_file(r->filename, temp_region_name);
1183 	  if (io_err == IO_NO_ERROR)
1184 	    {
1185 	      snd_info *sp;
1186 	      ss->open_requestor = FROM_REGION_EDIT;
1187 	      sp = snd_open_file(temp_region_name, FILE_READ_WRITE);
1188 	      if (sp)
1189 		{
1190 		  r->editor_copy = sp;
1191 		  r->editor_name = mus_strdup(temp_region_name);
1192 		  sp->edited_region = r;
1193 		  /* save backpointer so subsequent save affects region if still legit */
1194 		  /* also, since it's a temp file, if closed, delete temp */
1195 		}
1196 	      else snd_error("edit region: can't open region %d temp sound %s: %s!",
1197 			     r->id, temp_region_name, snd_io_strerror());
1198 	    }
1199 	  else
1200 	    snd_error("edit region: can't save region %d in temp file (%s: %s)",
1201 		      r->id, temp_region_name, snd_io_strerror());
1202 	  free(temp_region_name);
1203 	}
1204     }
1205   else snd_error("edit region: no region at position %d!", pos);
1206 }
1207 
1208 
clear_region_backpointer(snd_info * sp)1209 void clear_region_backpointer(snd_info *sp)
1210 {
1211   if (sp->edited_region)
1212     {
1213       region *r;
1214       r = sp->edited_region;
1215       if (r)
1216 	{
1217 	  snd_remove(r->editor_name, REMOVE_FROM_CACHE);
1218 	  free(r->editor_name);
1219 	  r->editor_name = NULL;
1220 	  r->editor_copy = NULL;
1221 	}
1222       sp->edited_region = NULL;
1223     }
1224 }
1225 
1226 
save_region_backpointer(snd_info * sp)1227 void save_region_backpointer(snd_info *sp)
1228 {
1229   /* region being edited, user chose 'save' */
1230   region *r;
1231   int i;
1232   io_error_t io_err;
1233 
1234   r = sp->edited_region;
1235   /* update r's data in file, deleting old, redisplay if browser active etc */
1236 
1237   if (r == regions[0]) deactivate_selection();
1238   free_region(r, CLEAR_REGION_DATA);
1239 
1240   r->use_temp_file = REGION_FILE;
1241   r->maxamp = 0.0;
1242   r->maxamp_position = -1;
1243   r->framples = current_samples(sp->chans[0]);
1244 
1245   for (i = 0; i < (int)sp->nchans; i++)
1246     {
1247       mus_float_t val;
1248       val = channel_maxamp(sp->chans[i], AT_CURRENT_EDIT_POSITION);
1249       if (val > r->maxamp) r->maxamp = val;
1250 
1251       if (r->ends[i] > PEAK_ENV_CUTOFF)
1252         {
1253           chan_info *cp;
1254           cp = sp->chans[i];
1255           if (!r->peak_envs)
1256             r->peak_envs = (peak_env_info **)calloc(r->chans, sizeof(peak_env_info *));
1257           else
1258 	    {
1259 	      if (r->peak_envs[i])
1260 		free_peak_env_info(r->peak_envs[i]);
1261 	    }
1262           /* if region file was edited, the peak_envs probably changed */
1263           r->peak_envs[i] = copy_peak_env_info(cp->edits[0]->peak_env, false);
1264         }
1265     }
1266 
1267   /* make new region temp file */
1268   r->filename = snd_tempnam();
1269   io_err = copy_file(r->editor_name, r->filename);
1270   if (io_err != IO_NO_ERROR)
1271     {
1272       if (io_err == IO_CANT_OPEN_FILE)
1273 	snd_error("can't find edited region temp file (%s: %s)", r->editor_name, snd_io_strerror());
1274       else snd_error("can't make region temp file (%s: %s)", r->filename, snd_io_strerror());
1275     }
1276   else
1277     {
1278       make_region_readable(r);
1279       if (region_browser_is_active())
1280 	update_region_browser(true);
1281     }
1282 }
1283 
1284 
save_region(int rg,const char * name,mus_sample_t samp_type,mus_header_t head_type,const char * comment)1285 io_error_t save_region(int rg, const char *name, mus_sample_t samp_type, mus_header_t head_type, const char *comment)
1286 {
1287   region *r;
1288   io_error_t io_err = IO_NO_ERROR;
1289 
1290   r = id_to_region(rg);
1291   if (r->use_temp_file == REGION_DEFERRED)
1292     deferred_region_to_temp_file(r);
1293 
1294   io_err = snd_write_header(name, head_type, region_srate(rg), r->chans, r->chans * r->framples, samp_type, comment, NULL);
1295   if (io_err == IO_NO_ERROR)
1296     {
1297       mus_long_t oloc;
1298       int ofd;
1299 
1300       oloc = mus_header_data_location();
1301       ofd = snd_reopen_write(name);
1302       if (ofd != -1)
1303 	{
1304 	  int ifd;
1305 	  snd_file_open_descriptors(ofd, name, samp_type, oloc, r->chans, head_type);
1306 	  mus_file_set_clipping(ofd, clipping(ss));
1307 	  lseek(ofd, oloc, SEEK_SET);
1308 	  /* copy r->filename with possible header/sample type changes */
1309 
1310 	  ifd = snd_open_read(r->filename);
1311 	  if (ifd != -1)
1312 	    {
1313 	      mus_long_t iloc, framples, cursamples;
1314 	      int chans, i, err = 0, ioff;
1315 	      mus_float_t **bufs;
1316 
1317 	      chans = mus_sound_chans(r->filename);
1318 	      framples = mus_sound_samples(r->filename) / chans;
1319 	      iloc = mus_sound_data_location(r->filename);
1320 
1321 	      snd_file_open_descriptors(ifd,
1322 					r->filename,
1323 					mus_sound_sample_type(r->filename),
1324 					iloc,
1325 					chans,
1326 					mus_sound_header_type(r->filename));
1327 	      lseek(ifd, iloc, SEEK_SET);
1328 	      bufs = (mus_float_t **)calloc(chans, sizeof(mus_float_t *));
1329 	      for (i = 0; i < chans; i++) bufs[i] = (mus_float_t *)calloc(FILE_BUFFER_SIZE, sizeof(mus_float_t));
1330 
1331 	      if (((framples * chans * mus_sound_datum_size(r->filename)) >> 10) > disk_kspace(name))
1332 		snd_warning("not enough space to save region? -- need %" print_mus_long " bytes",
1333 			    framples * chans * mus_sound_datum_size(r->filename));
1334 
1335 	      for (ioff = 0; ioff < framples; ioff += FILE_BUFFER_SIZE)
1336 		{
1337 		  if ((ioff + FILE_BUFFER_SIZE) < framples)
1338 		    cursamples = FILE_BUFFER_SIZE;
1339 		  else cursamples = (framples - ioff);
1340 		  mus_file_read(ifd, ioff, cursamples, chans, bufs);
1341 		  err = mus_file_write(ofd, 0, cursamples - 1, chans, bufs);
1342 		  if (err != MUS_NO_ERROR)
1343 		    {
1344 		      snd_warning("write error during %s", S_save_region);
1345 		      break;
1346 		    }
1347 		}
1348 	      err = mus_file_close(ifd);
1349 	      for (i = 0; i < chans; i++) free(bufs[i]);
1350 	      free(bufs);
1351 
1352 	      if (err != 0)
1353 		snd_warning("can't close %s input!", S_save_region);
1354 	      err = mus_file_close(ofd);
1355 
1356 	      if (ss->file_monitor_ok)
1357 		{
1358 		  if (err != 0)
1359 		    snd_error("%s %d: %s %s", S_save_region, rg, r->filename, snd_io_strerror());
1360 		}
1361 	      else
1362 		{
1363 #if USE_MOTIF
1364 		  if (err == 0)
1365 		    alert_new_file();
1366 		  else
1367 #else
1368 		  if (err != 0)
1369 #endif
1370 		     snd_error("%s %d: %s %s", S_save_region, rg, r->filename, snd_io_strerror());
1371 		}
1372 	    }
1373 	  else snd_error("%s %d: %s %s", S_save_region, rg, r->filename, snd_io_strerror());
1374 	}
1375       else snd_error("%s %d: %s %s", S_save_region, rg, name, snd_io_strerror());
1376     }
1377   else snd_error("%s %d: %s %s", S_save_region, rg, name, snd_io_strerror());
1378   return(io_err);
1379 }
1380 
1381 
1382 /* ---------------------------------------- region objects ---------------------------------------- */
1383 
1384 typedef struct {
1385   int n;
1386 } xen_region;
1387 
1388 
1389 #define Xen_to_xen_region(arg) ((xen_region *)Xen_object_ref(arg))
1390 
xen_region_to_int(Xen n)1391 int xen_region_to_int(Xen n)
1392 {
1393   xen_region *mx;
1394   mx = Xen_to_xen_region(n);
1395   return(mx->n);
1396 }
1397 
1398 
1399 static Xen_object_type_t xen_region_tag;
1400 
xen_is_region(Xen obj)1401 bool xen_is_region(Xen obj)
1402 {
1403   return(Xen_c_object_is_type(obj, xen_region_tag));
1404 }
1405 
1406 #if (!HAVE_SCHEME)
xen_region_free(xen_region * v)1407 static void xen_region_free(xen_region *v) {if (v) free(v);}
1408 
Xen_wrap_free(xen_region,free_xen_region,xen_region_free)1409 Xen_wrap_free(xen_region, free_xen_region, xen_region_free)
1410 #else
1411 static s7_pointer s7_xen_region_free(s7_scheme *sc, s7_pointer obj)
1412 {
1413   xen_region *v;
1414   v = (xen_region *)s7_c_object_value(obj);
1415   if (v) free(v);
1416   return(NULL);
1417 }
1418 #endif
1419 
1420 
1421 static char *xen_region_to_string(xen_region *v)
1422 {
1423   #define REGION_PRINT_BUFFER_SIZE 64
1424   char *buf;
1425   if (!v) return(NULL);
1426   buf = (char *)calloc(REGION_PRINT_BUFFER_SIZE, sizeof(char));
1427   snprintf(buf, REGION_PRINT_BUFFER_SIZE, "#<region %d>", v->n);
1428   return(buf);
1429 }
1430 
1431 
1432 #if HAVE_FORTH || HAVE_RUBY
Xen_wrap_print(xen_region,print_xen_region,xen_region_to_string)1433 Xen_wrap_print(xen_region, print_xen_region, xen_region_to_string)
1434 
1435 static Xen g_xen_region_to_string(Xen obj)
1436 {
1437   char *vstr;
1438   Xen result;
1439   #define S_xen_region_to_string "region->string"
1440   Xen_check_type(xen_is_region(obj), obj, 1, S_xen_region_to_string, "a region");
1441   vstr = xen_region_to_string(Xen_to_xen_region(obj));
1442   result = C_string_to_Xen_string(vstr);
1443   free(vstr);
1444   return(result);
1445 }
1446 #else
1447 #if HAVE_SCHEME
g_xen_region_to_string(s7_scheme * sc,s7_pointer args)1448 static s7_pointer g_xen_region_to_string(s7_scheme *sc, s7_pointer args)
1449 {
1450   char *vstr;
1451   Xen result;
1452   vstr = xen_region_to_string(Xen_to_xen_region(s7_car(args)));
1453   result = C_string_to_Xen_string(vstr);
1454   free(vstr);
1455   return(result);
1456 }
1457 #endif
1458 #endif
1459 
1460 
1461 #if (!HAVE_SCHEME)
xen_region_equalp(xen_region * v1,xen_region * v2)1462 static bool xen_region_equalp(xen_region *v1, xen_region *v2)
1463 {
1464   return((v1 == v2) ||
1465 	 (v1->n == v2->n));
1466 }
1467 
equalp_xen_region(Xen obj1,Xen obj2)1468 static Xen equalp_xen_region(Xen obj1, Xen obj2)
1469 {
1470   if ((!(xen_is_region(obj1))) || (!(xen_is_region(obj2)))) return(Xen_false);
1471   return(C_bool_to_Xen_boolean(xen_region_equalp(Xen_to_xen_region(obj1), Xen_to_xen_region(obj2))));
1472 }
1473 #endif
1474 
1475 
xen_region_make(int n)1476 static xen_region *xen_region_make(int n)
1477 {
1478   xen_region *new_v;
1479   new_v = (xen_region *)malloc(sizeof(xen_region));
1480   new_v->n = n;
1481   return(new_v);
1482 }
1483 
1484 
new_xen_region(int n)1485 Xen new_xen_region(int n)
1486 {
1487   xen_region *mx;
1488   if (n < 0)
1489     return(Xen_false);
1490 
1491   mx = xen_region_make(n);
1492   return(Xen_make_object(xen_region_tag, mx, 0, free_xen_region));
1493 }
1494 
1495 
1496 #if HAVE_SCHEME
s7_xen_region_is_equal(s7_scheme * sc,s7_pointer args)1497 static s7_pointer s7_xen_region_is_equal(s7_scheme *sc, s7_pointer args)
1498 {
1499   s7_pointer p1, p2;
1500   p1 = s7_car(args);
1501   p2 = s7_cadr(args);
1502   if (p1 == p2) return(s7_t(sc));
1503   if (s7_c_object_type(p2) == xen_region_tag)
1504     return(s7_make_boolean(sc, (((xen_region *)s7_c_object_value(p1))->n == ((xen_region *)s7_c_object_value(p2))->n)));
1505   return(s7_f(sc));
1506 }
1507 
s7_xen_region_length(s7_scheme * sc,Xen args)1508 static Xen s7_xen_region_length(s7_scheme *sc, Xen args)
1509 {
1510   return(g_region_framples(s7_car(args), Xen_integer_zero));
1511 }
1512 #endif
1513 
1514 
init_xen_region(void)1515 static void init_xen_region(void)
1516 {
1517 #if HAVE_SCHEME
1518   xen_region_tag = s7_make_c_type(s7, "<region>");
1519   s7_c_type_set_gc_free(s7, xen_region_tag, s7_xen_region_free);
1520   s7_c_type_set_is_equal(s7, xen_region_tag, s7_xen_region_is_equal);
1521   s7_c_type_set_length(s7, xen_region_tag, s7_xen_region_length);
1522   s7_c_type_set_to_string(s7, xen_region_tag, g_xen_region_to_string);
1523 #else
1524 #if HAVE_RUBY
1525   xen_region_tag = Xen_make_object_type("XenRegion", sizeof(xen_region));
1526 #else
1527   xen_region_tag = Xen_make_object_type("Region", sizeof(xen_region));
1528 #endif
1529 #endif
1530 
1531 #if HAVE_FORTH
1532   fth_set_object_inspect(xen_region_tag,   print_xen_region);
1533   fth_set_object_dump(xen_region_tag,      g_xen_region_to_string);
1534   fth_set_object_equal(xen_region_tag,     equalp_xen_region);
1535   fth_set_object_free(xen_region_tag,      free_xen_region);
1536 #endif
1537 
1538 #if HAVE_RUBY
1539   rb_define_method(xen_region_tag, "to_s",     Xen_procedure_cast print_xen_region, 0);
1540   rb_define_method(xen_region_tag, "eql?",     Xen_procedure_cast equalp_xen_region, 1);
1541   rb_define_method(xen_region_tag, "==",       Xen_procedure_cast equalp_xen_region, 1);
1542   rb_define_method(xen_region_tag, "to_str",   Xen_procedure_cast g_xen_region_to_string, 0);
1543 #endif
1544 }
1545 
1546 /* -------------------------------------------------------------------------------- */
1547 
1548 
g_integer_to_region(Xen n)1549 static Xen g_integer_to_region(Xen n)
1550 {
1551   #define H_integer_to_region "(" S_integer_to_region " n) returns a region object corresponding to the given integer"
1552   region* r;
1553   Xen_check_type(Xen_is_integer(n), n, 1, S_integer_to_region, "an integer");
1554   r = id_to_region(Xen_integer_to_C_int(n));
1555   if (r)
1556     return(new_xen_region(r->id));
1557   return(Xen_false);
1558 }
1559 
1560 
g_region_to_integer(Xen n)1561 static Xen g_region_to_integer(Xen n)
1562 {
1563   #define H_region_to_integer "(" S_region_to_integer " id) returns the integer corresponding to the given region"
1564   Xen_check_type(xen_is_region(n), n, 1, S_region_to_integer, "a region");
1565   return(C_int_to_Xen_integer(xen_region_to_int(n)));
1566 }
1567 
1568 
snd_no_such_region_error(const char * caller,Xen n)1569 static Xen snd_no_such_region_error(const char *caller, Xen n)
1570 {
1571   Xen_error(Xen_make_error_type("no-such-region"),
1572 	    Xen_list_3(C_string_to_Xen_string("~A: no such region, ~A"),
1573 		       C_string_to_Xen_string(caller),
1574 		       n));
1575   return(Xen_false);
1576 }
1577 
1578 
1579 /* Xen pos, Xen chans, Xen len, Xen srate, Xen maxamp, Xen name, Xen start, Xen end, Xen filename, Xen date */
1580 
g_restore_region(Xen args)1581 static Xen g_restore_region(Xen args)
1582 {
1583   /* internal function used by save-state mechanism -- not intended for external use */
1584   region *r;
1585   int i, regn;
1586   Xen arg, pos, chans, len, srate, maxamp, name, start, end, filename, date;
1587 
1588   Xen_check_type(Xen_list_length(args) == 10, args, 0, S_restore_region, "10 items");
1589   arg = args;
1590   pos = Xen_car(arg);
1591   Xen_check_type(Xen_is_integer(pos), pos, 1, S_restore_region, "a region id");
1592 
1593   arg = Xen_cdr(arg);
1594   chans = Xen_car(arg);
1595   Xen_check_type(Xen_is_integer(chans), chans, 2, S_restore_region, "an integer");
1596 
1597   arg = Xen_cdr(arg);
1598   len = Xen_car(arg);
1599   Xen_check_type(Xen_is_integer(len), len, 3, S_restore_region, "an integer");
1600 
1601   arg = Xen_cdr(arg);
1602   srate = Xen_car(arg);
1603   Xen_check_type(Xen_is_integer(srate), srate, 4, S_restore_region, "an integer");
1604 
1605   arg = Xen_cdr(arg);
1606   maxamp = Xen_car(arg);
1607   Xen_check_type(Xen_is_double(maxamp), maxamp, 5, S_restore_region, "a double");
1608 
1609   arg = Xen_cdr(arg);
1610   name = Xen_car(arg);
1611   Xen_check_type(Xen_is_string(name), name, 6, S_restore_region, "a string");
1612 
1613   arg = Xen_cdr(arg);
1614   start = Xen_car(arg);
1615   Xen_check_type(Xen_is_string(start), start, 7, S_restore_region, "a string");
1616 
1617   arg = Xen_cdr(arg);
1618   end = Xen_car(arg);
1619   Xen_check_type(Xen_is_string(end), end, 8, S_restore_region, "a string");
1620 
1621   arg = Xen_cdr(arg);
1622   filename = Xen_car(arg);
1623   Xen_check_type(Xen_is_string(filename), filename, 9, S_restore_region, "a string");
1624 
1625   arg = Xen_cdr(arg);
1626   date = Xen_car(arg);
1627   Xen_check_type(Xen_is_list(date) && (Xen_list_length(date) == 2), date, 10, S_restore_region, "a list: '(time bytes)");
1628 
1629   check_saved_temp_file("region", filename, date);
1630 
1631   r = (region *)calloc(1, sizeof(region));
1632   regn = Xen_integer_to_C_int(pos);
1633   if (regions[regn]) free_region(regions[regn], COMPLETE_DELETION);
1634   regions[regn] = r;
1635   r->id = region_id_ctr++;
1636   r->maxamp = Xen_real_to_C_double(maxamp);
1637   r->maxamp_position = -1; /* not saved/restored */
1638   r->chans = Xen_integer_to_C_int(chans);
1639   r->rsp = NULL;
1640   r->editor_copy = NULL;
1641   r->editor_name = NULL;
1642   r->framples = Xen_llong_to_C_llong(len);
1643   r->srate = Xen_integer_to_C_int(srate);
1644   r->name = mus_strdup(Xen_string_to_C_string(name));
1645   r->start = mus_strdup(Xen_string_to_C_string(start));
1646   r->end = mus_strdup(Xen_string_to_C_string(end));
1647   r->use_temp_file = REGION_FILE;
1648   r->filename = mus_strdup(Xen_string_to_C_string(filename));
1649 
1650   /* bugfix for saved regions -- thanks to Tito Latini */
1651   r->begs = (mus_long_t *)calloc(r->chans, sizeof(mus_long_t));
1652   r->ends = (mus_long_t *)calloc(r->chans, sizeof(mus_long_t));
1653   for (i = 0; i < r->chans; i++)
1654     {
1655       r->begs[i] = 0;
1656       r->ends[i] = r->framples - 1;
1657     }
1658 
1659   reflect_regions_in_region_browser();
1660   return(C_int_to_Xen_integer(r->id));
1661 }
1662 
1663 
g_insert_region(Xen reg_n,Xen samp_n,Xen snd_n,Xen chn_n)1664 static Xen g_insert_region(Xen reg_n, Xen samp_n, Xen snd_n, Xen chn_n) /* opt reg_n */
1665 {
1666   #define H_insert_region "("  S_insert_region " region :optional (start-samp 0) snd chn): \
1667 insert region data into snd's channel chn starting at start-samp"
1668 
1669   chan_info *cp;
1670   int rg;
1671   mus_long_t samp;
1672   io_error_t err = IO_NO_ERROR;
1673 
1674   Xen_check_type(xen_is_region(reg_n), reg_n, 1, S_insert_region, "a region id");
1675   Xen_check_type(Xen_is_integer_or_unbound(samp_n), samp_n, 2, S_insert_region, "an integer");
1676 
1677   Snd_assert_channel(S_insert_region, snd_n, chn_n, 3);
1678   cp = get_cp(snd_n, chn_n, S_insert_region);
1679   if (!cp) return(Xen_false);
1680 
1681   rg = Xen_region_to_C_int(reg_n);
1682   if (!(region_ok(rg)))
1683     return(snd_no_such_region_error(S_insert_region, reg_n));
1684 
1685   samp = beg_to_sample(samp_n, S_insert_region);
1686   err = paste_region_2(rg, cp, false, samp);
1687 
1688   if (is_serious_io_error(err))
1689     Xen_error(Xen_make_error_type("IO-error"),
1690 	      Xen_list_3(C_string_to_Xen_string(S_insert_region ": can't edit ~A (region access problem?), ~A"),
1691 			 C_string_to_Xen_string(cp->sound->filename),
1692 			 C_string_to_Xen_string(io_error_name(err))));
1693   update_graph(cp);
1694   return(reg_n);
1695 }
1696 
1697 
g_max_regions(void)1698 static Xen g_max_regions(void)
1699 {
1700   #define H_max_regions "(" S_max_regions "): max number of regions saved on the region list"
1701   return(C_int_to_Xen_integer(max_regions(ss)));
1702 }
1703 
1704 
g_set_max_regions(Xen n)1705 static Xen g_set_max_regions(Xen n)
1706 {
1707   int regs;
1708   Xen_check_type(Xen_is_integer(n), n, 1, S_set S_max_regions, "an integer");
1709 
1710   regs = Xen_integer_to_C_int(n);
1711   if (regs < 0)
1712     Xen_out_of_range_error(S_set S_max_regions, 1, n, S_max_regions "negative?");
1713 
1714   set_max_regions(regs);
1715   return(C_int_to_Xen_integer(max_regions(ss)));
1716 }
1717 
1718 
g_is_region(Xen n)1719 static Xen g_is_region(Xen n)
1720 {
1721   #define H_is_region "(" S_is_region " reg): " PROC_TRUE " if region is active"
1722   return(C_bool_to_Xen_boolean((xen_is_region(n)) && (region_ok(Xen_region_to_C_int(n)))));
1723 }
1724 
1725 
g_region_framples(Xen n,Xen chan)1726 Xen g_region_framples(Xen n, Xen chan)
1727 {
1728   region *r;
1729   int rg, chn;
1730   #define H_region_framples "(" S_region_framples " reg :optional (chan 0)): region length in framples"
1731 
1732   Xen_check_type(xen_is_region(n), n, 1, S_region_framples, "a region");
1733   Xen_check_type(Xen_is_integer_or_unbound(chan), chan, 2, S_region_framples, "an integer");
1734 
1735   rg = Xen_region_to_C_int(n);
1736   if (!(region_ok(rg)))
1737     return(snd_no_such_region_error(S_region_framples, n));
1738 
1739   if (!Xen_is_bound(chan))
1740     return(C_llong_to_Xen_llong(region_len(rg)));
1741   chn = Xen_integer_to_C_int(chan);
1742   if ((chn < 0) || (chn >= region_chans(rg)))
1743     return(snd_no_such_channel_error(S_region_framples, Xen_list_1(n), chan));
1744 
1745   r = id_to_region(rg);
1746   return(C_llong_to_Xen_llong(r->ends[chn] + 1));
1747 }
1748 
1749 
g_region_position(Xen n,Xen chan)1750 static Xen g_region_position(Xen n, Xen chan)
1751 {
1752   region *r;
1753   int rg, chn = 0;
1754   #define H_region_position "(" S_region_position " reg :optional (chan 0)): region's position in the original sound"
1755 
1756   Xen_check_type(xen_is_region(n), n, 1, S_region_position, "a region");
1757   Xen_check_type(Xen_is_integer_or_unbound(chan), chan, 2, S_region_position, "an integer");
1758 
1759   rg = Xen_region_to_C_int(n);
1760   if (!(region_ok(rg)))
1761     return(snd_no_such_region_error(S_region_position, n));
1762 
1763   if (Xen_is_integer(chan)) chn = Xen_integer_to_C_int(chan);
1764   if ((chn < 0) || (chn >= region_chans(rg)))
1765     return(snd_no_such_channel_error(S_region_position, Xen_list_1(n), chan));
1766 
1767   r = id_to_region(rg);
1768   return(C_llong_to_Xen_llong(r->begs[chn]));
1769 }
1770 
1771 
1772 typedef enum {REGION_SRATE, REGION_CHANS, REGION_MAXAMP, REGION_FORGET, REGION_MAXAMP_POSITION, REGION_HOME} region_field_t;
1773 
region_get(region_field_t field,Xen n,const char * caller)1774 static Xen region_get(region_field_t field, Xen n, const char *caller)
1775 {
1776   int rg;
1777   rg = Xen_region_to_C_int(n);
1778   if (!(region_ok(rg)))
1779     return(snd_no_such_region_error(caller, n));
1780 
1781   switch (field)
1782     {
1783     case REGION_SRATE:  return(C_int_to_Xen_integer(region_srate(rg)));
1784     case REGION_CHANS:  return(C_int_to_Xen_integer(region_chans(rg)));
1785     case REGION_MAXAMP: return(C_double_to_Xen_real(region_maxamp(rg)));
1786     case REGION_MAXAMP_POSITION: return(C_llong_to_Xen_llong(region_maxamp_position(rg)));
1787     case REGION_FORGET: delete_region_and_update_browser(region_id_to_list_position(rg)); return(n);
1788     case REGION_HOME:
1789       {
1790 	region *r;
1791 	r = id_to_region(rg);
1792 	if (r)
1793 	  return(Xen_list_3(C_string_to_Xen_string(r->name),
1794 			    C_llong_to_Xen_llong(r->begs[0]),
1795 			    C_llong_to_Xen_llong(r->ends[0] + 1)));
1796       }
1797       break;
1798     default: break;
1799     }
1800   return(Xen_false);
1801 }
1802 
1803 
g_region_srate(Xen n)1804 Xen g_region_srate(Xen n)
1805 {
1806   #define H_region_srate "(" S_region_srate " reg): region (nominal) srate"
1807   Xen_check_type(xen_is_region(n), n, 1, S_region_srate, "a region");
1808   return(region_get(REGION_SRATE, n, S_region_srate));
1809 }
1810 
1811 
g_region_chans(Xen n)1812 Xen g_region_chans(Xen n)
1813 {
1814   #define H_region_chans "(" S_region_chans " reg): region channels"
1815   Xen_check_type(xen_is_region(n), n, 1, S_region_chans, "a region");
1816   return(region_get(REGION_CHANS, n, S_region_chans));
1817 }
1818 
1819 
g_region_home(Xen n)1820 static Xen g_region_home(Xen n)
1821 {
1822   #define H_region_home "(" S_region_home " reg): a list with the region source sound name and position info"
1823   Xen_check_type(xen_is_region(n), n, 1, S_region_home, "a region");
1824   return(region_get(REGION_HOME, n, S_region_home));
1825 }
1826 
1827 
g_region_maxamp(Xen n)1828 Xen g_region_maxamp(Xen n)
1829 {
1830   #define H_region_maxamp "(" S_region_maxamp " reg): region maxamp"
1831   Xen_check_type(xen_is_region(n), n, 1, S_region_maxamp, "a region");
1832   return(region_get(REGION_MAXAMP, n, S_region_maxamp));
1833 }
1834 
1835 
g_region_maxamp_position(Xen n)1836 static Xen g_region_maxamp_position(Xen n)
1837 {
1838   #define H_region_maxamp_position "(" S_region_maxamp_position " reg): first sample where region maxamp occurs"
1839   Xen_check_type(xen_is_region(n), n, 1, S_region_maxamp_position, "a region");
1840   return(region_get(REGION_MAXAMP_POSITION, n, S_region_maxamp_position));
1841 }
1842 
1843 
g_forget_region(Xen n)1844 static Xen g_forget_region(Xen n)
1845 {
1846   #define H_forget_region "(" S_forget_region " reg): remove region from the region list"
1847   Xen_check_type(xen_is_region(n), n, 1, S_forget_region, "a region");
1848   return(region_get(REGION_FORGET, n, S_forget_region));
1849 }
1850 
1851 
g_play_region(Xen n,play_process_t back,Xen stop_proc)1852 Xen g_play_region(Xen n, play_process_t back, Xen stop_proc)
1853 {
1854   int rg;
1855   region *r;
1856   rg = Xen_region_to_C_int(n);
1857   r = id_to_region(rg);
1858   if (r)
1859     {
1860       make_region_readable(r);
1861       play_region_1(rg, back, stop_proc);
1862       return(n);
1863     }
1864   return(snd_no_such_region_error(S_play, n));
1865 }
1866 
1867 
g_regions(void)1868 static Xen g_regions(void)
1869 {
1870   #define H_regions "(" S_regions "): current active regions (a list of region ids)"
1871   int i;
1872   Xen result;
1873   result = Xen_empty_list;
1874   for (i = (regions_size - 1); i >= 0; i--)
1875     if (regions[i])
1876       result = Xen_cons(C_int_to_Xen_region(regions[i]->id), result);
1877   return(result);
1878 }
1879 
1880 
g_make_region(Xen beg,Xen end,Xen snd_n,Xen chn_n)1881 static Xen g_make_region(Xen beg, Xen end, Xen snd_n, Xen chn_n)
1882 {
1883   #define H_make_region "(" S_make_region " :optional beg end snd chn): make a new region between beg and end in snd. \
1884 If chn is " PROC_TRUE ", all chans are included, taking the snd sync field into account if it's not 0.  If no args are passed, the current \
1885 selection is used."
1886   int id = INVALID_REGION;
1887 
1888   if (max_regions(ss) <= 0) return(Xen_false);
1889   if (!Xen_is_bound(beg))
1890     id = make_region_from_selection();
1891   else
1892     {
1893       sync_info *si = NULL;
1894       snd_info *sp;
1895       mus_long_t ibeg, iend;
1896       mus_long_t *ends = NULL;
1897       int i;
1898 
1899       Xen_check_type(Xen_is_integer(beg), beg, 1, S_make_region, "an integer");
1900       Xen_check_type(Xen_is_integer(end), end, 2, S_make_region, "an integer");
1901 
1902       ibeg = beg_to_sample(beg, S_make_region);
1903       iend = beg_to_sample(end, S_make_region);
1904 
1905       if (Xen_is_true(chn_n))
1906 	{
1907 	  /* all chans and all sync'd chans if sync not 0 */
1908 	  sp = get_sp(snd_n);
1909 	  if (sp)
1910 	    {
1911 	      int old_sync;
1912 	      old_sync = sp->sync;
1913 	      if (sp->sync == 0)
1914 		{
1915 		  /* set up temp sync for snd_sync */
1916 		  sp->sync = ss->sound_sync_max + 1;
1917 		  ss->sound_sync_max++;
1918 		}
1919 	      si = snd_sync(sp->sync);
1920 	      sp->sync = old_sync;
1921 	    }
1922 	  else return(snd_no_such_sound_error(S_make_region, snd_n));
1923 	}
1924       else
1925 	{
1926 	  chan_info *cp;
1927 	  cp = get_cp(snd_n, chn_n, S_make_region);
1928 	  si = make_simple_sync(cp, ibeg);
1929 	}
1930 
1931       ends = (mus_long_t *)calloc(si->chans, sizeof(mus_long_t));
1932       for (i = 0; i < si->chans; i++)
1933 	{
1934 	  if (current_samples(si->cps[i]) - 1 < iend)
1935 	    ends[i] = current_samples(si->cps[i]) - 1;
1936 	  else ends[i] = iend;
1937 	  si->begs[i] = ibeg;
1938 	  if (ends[i] < ibeg)
1939 	    {
1940 	      free(ends);
1941 	      ends = NULL;
1942 	      si = free_sync_info(si);
1943 	      Xen_out_of_range_error(S_make_region, 1, end, "end < beg?");
1944 	    }
1945 	}
1946       if (ends)
1947 	{
1948 	  id = define_region(si, ends);
1949 	  if (selection_creates_region(ss))
1950 	    reactivate_selection(si->cps[0], si->begs[0], ends[0]);
1951 	  free_sync_info(si);
1952 	  free(ends);
1953 	}
1954     }
1955   return(C_int_to_Xen_region(id));
1956 }
1957 
1958 
save_region_to_xen_error(const char * msg,void * data)1959 static void save_region_to_xen_error(const char *msg, void *data)
1960 {
1961   redirect_snd_error_to(NULL, NULL);
1962   Xen_error(Xen_make_error_type("cannot-save"),
1963 	    Xen_list_2(C_string_to_Xen_string(S_save_region ": can't save ~S"),
1964 		       C_string_to_Xen_string(msg)));
1965 }
1966 
1967 #define H_save_region "(" S_save_region " region file sample-type header-type comment): save region in file \
1968 using sample-type (default depends on machine byte order), header-type (" S_mus_next "), and comment"
1969 
1970 #if HAVE_SCHEME
g_save_region(s7_scheme * sc,s7_pointer args)1971 static s7_pointer g_save_region(s7_scheme *sc, s7_pointer args)
1972 {
1973   mus_header_t head_type;
1974   mus_sample_t samp_type;
1975   int rg;
1976   const char *com, *file;
1977   char *name;
1978   s7_pointer p, fp, res;
1979 
1980   fp = s7_car(args);
1981   if (!xen_is_region(fp))
1982     return(s7_wrong_type_arg_error(sc, S_save_region, 1, fp, "a region"));
1983   rg = Xen_region_to_C_int(fp);
1984   if (!(region_ok(rg)))
1985     return(snd_no_such_region_error(S_save_region, fp));
1986 
1987   fp = s7_cadr(args);
1988   if (fp == Xen_false)
1989     Xen_error(Xen_make_error_type("IO-error"),
1990 	      Xen_list_1(C_string_to_Xen_string(S_save_region ": no output file?")));
1991   if (!s7_is_string(fp))
1992     return(s7_wrong_type_arg_error(sc, S_save_region, 2, fp, "a string"));
1993   file = s7_string(fp);
1994   name = mus_expand_filename(file);
1995   res = fp;
1996 
1997   p = s7_cddr(args);
1998   fp = s7_car(p);
1999   if (fp == Xen_false)
2000     samp_type = MUS_OUT_SAMPLE_TYPE;
2001   else
2002     {
2003       if (!s7_is_integer(fp))
2004 	return(s7_wrong_type_arg_error(sc, S_save_selection, 3, fp, "an integer"));
2005       samp_type = (mus_sample_t)s7_integer(fp);
2006     }
2007 
2008   fp = s7_cadr(p);
2009   if (fp == Xen_false)
2010     head_type = MUS_NEXT;
2011   else
2012     {
2013       if (!s7_is_integer(fp))
2014 	return(s7_wrong_type_arg_error(sc, S_save_selection, 4, fp, "an integer"));
2015       head_type = (mus_header_t)s7_integer(fp);
2016     }
2017 
2018   if (!(mus_header_writable(head_type, samp_type)))
2019     {
2020       if (mus_header_writable(MUS_NEXT, samp_type))
2021 	head_type = MUS_NEXT;
2022       else
2023 	{
2024 	  if (mus_header_writable(MUS_RIFF, samp_type))
2025 	    head_type = MUS_RIFF;
2026 	  else head_type = MUS_RAW;
2027 	}
2028     }
2029 
2030   fp = s7_caddr(p);
2031   if (fp == Xen_false)
2032     com = NULL;
2033   else
2034     {
2035       if (!s7_is_string(fp))
2036 	return(s7_wrong_type_arg_error(sc, S_save_selection, 5, fp, "a string"));
2037       com = s7_string(fp);
2038     }
2039 
2040   redirect_snd_error_to(save_region_to_xen_error, NULL); /* could perhaps pass name here for free in case of error */
2041   save_region(rg, name, samp_type, head_type, com);
2042   redirect_snd_error_to(NULL, NULL);
2043 
2044   if (name) free(name);
2045   return(res);
2046 }
2047 #else
2048 static Xen kw_header_type, kw_comment, kw_file, kw_sample_type;
2049 
init_region_keywords(void)2050 static void init_region_keywords(void)
2051 {
2052   kw_header_type = Xen_make_keyword("header-type");
2053   kw_sample_type = Xen_make_keyword("sample-type");
2054   kw_comment = Xen_make_keyword("comment");
2055   kw_file = Xen_make_keyword("file");
2056 }
2057 
g_save_region(Xen n,Xen arg1,Xen arg2,Xen arg3,Xen arg4,Xen arg5,Xen arg6,Xen arg7,Xen arg8)2058 static Xen g_save_region(Xen n, Xen arg1, Xen arg2, Xen arg3, Xen arg4, Xen arg5, Xen arg6, Xen arg7, Xen arg8)
2059 {
2060   char *name = NULL;
2061   const char *file = NULL, *com = NULL;
2062   int rg, true_args;
2063   mus_sample_t sample_type = MUS_OUT_SAMPLE_TYPE;
2064   mus_header_t header_type = MUS_NEXT;
2065   Xen args[8];
2066   Xen keys[4];
2067   int orig_arg[4] = {0, 0, 0, 0};
2068   int vals;
2069 
2070   keys[0] = kw_file;
2071   keys[1] = kw_sample_type;
2072   keys[2] = kw_header_type;
2073   keys[3] = kw_comment;
2074   args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = arg7; args[7] = arg8;
2075   for (true_args = 0; true_args < 8; true_args++)
2076     if (!Xen_is_bound(args[true_args]))
2077       break;
2078   Xen_check_type(xen_is_region(n), n, 1, S_save_region, "a region id");
2079 
2080   rg = Xen_region_to_C_int(n);
2081   if (!(region_ok(rg)))
2082     return(snd_no_such_region_error(S_save_region, n));
2083 
2084   vals = mus_optkey_unscramble(S_save_region, true_args, 4, keys, args, orig_arg);
2085   if (vals > 0)
2086     {
2087       file = mus_optkey_to_string(keys[0], S_save_region, orig_arg[0], NULL);
2088       sample_type = (mus_sample_t)mus_optkey_to_int(keys[1], S_save_region, orig_arg[1], (int)sample_type);
2089       header_type = (mus_header_t)mus_optkey_to_int(keys[2], S_save_region, orig_arg[2], (int)header_type);
2090       com = mus_optkey_to_string(keys[3], S_save_region, orig_arg[3], NULL);
2091     }
2092 
2093   if (!file)
2094     Xen_error(Xen_make_error_type("IO-error"),
2095 	      Xen_list_1(C_string_to_Xen_string(S_save_region ": no output file?")));
2096 
2097   name = mus_expand_filename(file);
2098 
2099   if (!(mus_header_writable(header_type, sample_type)))
2100     {
2101       if (mus_header_writable(MUS_NEXT, sample_type))
2102 	header_type = MUS_NEXT;
2103       else
2104 	{
2105 	  if (mus_header_writable(MUS_RIFF, sample_type))
2106 	    header_type = MUS_RIFF;
2107 	  else header_type = MUS_RAW;
2108 	}
2109     }
2110 
2111   redirect_snd_error_to(save_region_to_xen_error, NULL); /* could perhaps pass name here for free in case of error */
2112   save_region(rg, name, sample_type, header_type, com);
2113   redirect_snd_error_to(NULL, NULL);
2114 
2115   if (name) free(name);
2116   return(args[orig_arg[0] - 1]); /* -> filename, parallel save-selection */
2117 }
2118 #endif
2119 
g_mix_region(Xen reg_n,Xen chn_samp_n,Xen snd_n,Xen chn_n,Xen reg_chn)2120 static Xen g_mix_region(Xen reg_n, Xen chn_samp_n, Xen snd_n, Xen chn_n, Xen reg_chn)
2121 {
2122   #define H_mix_region "(" S_mix_region " region :optional (chn-samp 0) snd chn (region-chan " PROC_TRUE ")): \
2123 mix region's channel region-chan (or all chans if region-chan is " PROC_TRUE ") into snd's channel chn starting at chn-samp; \
2124 it returns a list of the new mixes"
2125 
2126   chan_info *cp;
2127   mus_long_t samp;
2128   io_error_t err = IO_NO_ERROR;
2129   int i, rg, id = -1, reg_chan = 0, reg_chans = 0;
2130   Xen result = Xen_empty_list;
2131 
2132   Xen_check_type(xen_is_region(reg_n), reg_n, 1, S_mix_region, "a region");
2133   Xen_check_type(Xen_is_integer_or_unbound(chn_samp_n), chn_samp_n, 2, S_mix_region, "an integer");
2134   Xen_check_type(Xen_is_integer_boolean_or_unbound(reg_chn), reg_chn, 5, S_mix_region, "an integer or " PROC_TRUE);
2135   Snd_assert_channel(S_mix_region, snd_n, chn_n, 3);
2136 
2137   rg = Xen_region_to_C_int(reg_n);
2138   if (!(region_ok(rg)))
2139     return(snd_no_such_region_error(S_mix_region, reg_n));
2140 
2141   cp = get_cp(snd_n, chn_n, S_mix_region);
2142   if (!cp) return(Xen_false);
2143 
2144   if (Xen_is_bound(chn_samp_n))
2145     samp = beg_to_sample(chn_samp_n, S_mix_region);
2146   else samp = cursor_sample(cp);
2147 
2148   if (Xen_is_integer(reg_chn))
2149     reg_chan = Xen_integer_to_C_int(reg_chn);
2150 
2151   id = paste_region_1(rg, cp, true, samp, &err, reg_chan, &reg_chans);
2152 
2153   /* id might legitmately be invalid mix id if with_mix_tags is #f or virtual mix not ok */
2154   if (is_serious_io_error(err))
2155     Xen_error(Xen_make_error_type("IO-error"),
2156 	      Xen_list_2(C_string_to_Xen_string(S_mix_region ": can't edit, ~A"),
2157 			 C_string_to_Xen_string(io_error_name(err))));
2158 
2159   if (id == -1) return(Xen_false);
2160   for (i = 0; i < reg_chans; i++)
2161     result = Xen_cons(new_xen_mix(id + i), result);
2162 
2163   return(Xen_list_reverse(result));
2164 }
2165 
2166 
g_region_sample(Xen reg_n,Xen samp_n,Xen chn_n)2167 static Xen g_region_sample(Xen reg_n, Xen samp_n, Xen chn_n)
2168 {
2169   #define H_region_sample "(" S_region_sample " region samp :optional (chan 0)): region's sample at samp in chan"
2170 
2171   int rg, chan = 0;
2172   mus_long_t samp;
2173 
2174   Xen_check_type(xen_is_region(reg_n), reg_n, 1, S_region_sample, "a region");
2175   Xen_check_type(Xen_is_integer(samp_n), samp_n, 2, S_region_sample, "an integer");
2176   Xen_check_type(Xen_is_integer_or_unbound(chn_n), chn_n, 3, S_region_sample, "an integer");
2177 
2178   if (Xen_is_integer(chn_n)) chan = Xen_integer_to_C_int(chn_n);
2179   rg = Xen_region_to_C_int(reg_n);
2180   if (!(region_ok(rg)))
2181     return(snd_no_such_region_error(S_region_sample, reg_n));
2182 
2183   samp = beg_to_sample(samp_n, S_region_sample);
2184   if ((chan >= 0) && (chan < region_chans(rg)))
2185     return(C_double_to_Xen_real(region_sample(rg, chan, samp)));
2186   return(snd_no_such_channel_error(S_region_sample, Xen_list_1(reg_n), chn_n));
2187 }
2188 
2189 
g_region_to_vct(Xen reg_n,Xen beg_n,Xen num,Xen chn_n,Xen v)2190 Xen g_region_to_vct(Xen reg_n, Xen beg_n, Xen num, Xen chn_n, Xen v)
2191 {
2192   #define H_region_to_vct "(" S_region_to_vct " region :optional (beg 0) samps (chan 0) v): \
2193 write region's samples starting at beg for samps in channel chan to " S_vct " v; return v (or create a new one)"
2194 
2195   int reg, chn = 0;
2196   mus_long_t len = 0;
2197   vct *v1 = xen_to_vct(v);
2198 
2199   Xen_check_type(xen_is_region(reg_n), reg_n, 1, S_region_to_vct, "a region");
2200   Xen_check_type(Xen_is_integer_or_unbound(beg_n), beg_n, 2, S_region_to_vct, "an integer");
2201   Xen_check_type(Xen_is_integer_or_unbound(num), num, 3, S_region_to_vct, "an integer");
2202   Xen_check_type(Xen_is_integer_or_unbound(chn_n), chn_n, 4, S_region_to_vct, "an integer");
2203 
2204   reg = Xen_region_to_C_int(reg_n);
2205   if (!(region_ok(reg)))
2206     return(snd_no_such_region_error(S_region_to_vct, reg_n));
2207 
2208   if (Xen_is_integer(chn_n))
2209     {
2210       chn = Xen_integer_to_C_int(chn_n);
2211       if ((chn < 0) || (chn >= region_chans(reg)))
2212 	return(snd_no_such_channel_error(S_region_to_vct, Xen_list_1(reg_n), chn_n));
2213     }
2214   if (Xen_is_integer(num))
2215     {
2216       len = Xen_llong_to_C_llong(num);
2217       if (len < 0)
2218 	Xen_out_of_range_error(S_region_to_vct, 2, num, "length < 0?");
2219     }
2220   if ((len == 0) || (len > region_len(reg)))
2221     len = region_len(reg);
2222 
2223   if (len > 0)
2224     {
2225       mus_float_t *data;
2226       mus_long_t beg;
2227       beg = beg_to_sample(beg_n, S_region_to_vct);
2228       if (beg >= region_len(reg))
2229 	return(Xen_false);
2230       if (v1)
2231 	{
2232 	  data = mus_vct_data(v1);
2233 	  if (len > mus_vct_length(v1)) len = mus_vct_length(v1);
2234 	}
2235       else
2236 	{
2237 	  if ((beg + len) > region_len(reg))
2238 	    len = region_len(reg) - beg;
2239 	  data = (mus_float_t *)calloc(len, sizeof(mus_float_t));
2240 	}
2241       region_samples(reg, chn, beg, len, data);
2242       if (v1)
2243 	return(v);
2244       else return(xen_make_vct(len, data));
2245     }
2246   return(Xen_false);
2247 }
2248 
2249 
g_region_graph_style(void)2250 static Xen g_region_graph_style(void) {return(C_int_to_Xen_integer(region_graph_style(ss)));}
2251 
g_set_region_graph_style(Xen val)2252 static Xen g_set_region_graph_style(Xen val)
2253 {
2254   int style;
2255   #define H_region_graph_style "(" S_region_graph_style "): graph style of the region dialog graph. \
2256 The " S_region_graph_style " choices are " S_graph_lines ", " S_graph_dots ", " S_graph_filled ", " S_graph_lollipops ", \
2257 and " S_graph_dots_and_lines "."
2258 
2259   Xen_check_type(Xen_is_integer(val), val, 1, S_set S_region_graph_style, "an integer");
2260 
2261   style = Xen_integer_to_C_int(val);
2262   if (!is_graph_style(style))
2263     Xen_out_of_range_error(S_set S_region_graph_style, 1, val, "unknown " S_lisp_graph_style);
2264 
2265   set_region_graph_style((graph_style_t)style);
2266   reflect_region_graph_style();
2267   return(val);
2268 }
2269 
2270 
Xen_wrap_any_args(g_restore_region_w,g_restore_region)2271 Xen_wrap_any_args(g_restore_region_w, g_restore_region)
2272 Xen_wrap_4_optional_args(g_insert_region_w, g_insert_region)
2273 Xen_wrap_no_args(g_regions_w, g_regions)
2274 Xen_wrap_2_optional_args(g_region_framples_w, g_region_framples)
2275 Xen_wrap_2_optional_args(g_region_position_w, g_region_position)
2276 Xen_wrap_1_arg(g_region_srate_w, g_region_srate)
2277 Xen_wrap_1_arg(g_region_chans_w, g_region_chans)
2278 Xen_wrap_1_arg(g_region_home_w, g_region_home)
2279 Xen_wrap_1_arg(g_region_maxamp_w, g_region_maxamp)
2280 Xen_wrap_1_arg(g_region_maxamp_position_w, g_region_maxamp_position)
2281 Xen_wrap_1_arg(g_forget_region_w, g_forget_region)
2282 Xen_wrap_4_optional_args(g_make_region_w, g_make_region)
2283 Xen_wrap_5_optional_args(g_mix_region_w, g_mix_region)
2284 Xen_wrap_3_optional_args(g_region_sample_w, g_region_sample)
2285 Xen_wrap_5_optional_args(g_region_to_vct_w, g_region_to_vct)
2286 Xen_wrap_1_arg(g_is_region_w, g_is_region)
2287 Xen_wrap_no_args(g_max_regions_w, g_max_regions)
2288 Xen_wrap_1_arg(g_set_max_regions_w, g_set_max_regions)
2289 Xen_wrap_no_args(g_region_graph_style_w, g_region_graph_style)
2290 Xen_wrap_1_arg(g_set_region_graph_style_w, g_set_region_graph_style)
2291 Xen_wrap_1_arg(g_integer_to_region_w, g_integer_to_region)
2292 Xen_wrap_1_arg(g_region_to_integer_w, g_region_to_integer)
2293 
2294 #if HAVE_SCHEME
2295 static s7_pointer acc_region_graph_style(s7_scheme *sc, s7_pointer args) {return(g_set_region_graph_style(s7_cadr(args)));}
acc_max_regions(s7_scheme * sc,s7_pointer args)2296 static s7_pointer acc_max_regions(s7_scheme *sc, s7_pointer args) {return(g_set_max_regions(s7_cadr(args)));}
2297 #else
2298 Xen_wrap_9_optional_args(g_save_region_w, g_save_region)
2299 #endif
2300 
2301 
g_init_regions(void)2302 void g_init_regions(void)
2303 {
2304 #if HAVE_SCHEME
2305   s7_pointer i, b, p, t, r, f, s, fv, rg;
2306   i = s7_make_symbol(s7, "integer?");
2307   rg = s7_make_symbol(s7, "region?");
2308   b = s7_make_symbol(s7, "boolean?");
2309   p = s7_make_symbol(s7, "list?");
2310   f = s7_make_symbol(s7, "float?");
2311   r = s7_make_symbol(s7, "real?");
2312   s = s7_make_symbol(s7, "string?");
2313   fv = s7_make_symbol(s7, "float-vector?");
2314   t = s7_t(s7);
2315 #endif
2316 
2317   init_xen_region();
2318 #if (!HAVE_SCHEME)
2319   init_region_keywords();
2320 #endif
2321 
2322   Xen_define_typed_procedure(S_restore_region,         g_restore_region_w,         0, 0, 1, "internal func used in save-state, restores a region",
2323 			     s7_make_signature(s7, 11, i, i, i, i, i, r, s, s, s, s, p));
2324   Xen_define_typed_procedure(S_insert_region,          g_insert_region_w,          2, 2, 0, H_insert_region,   s7_make_signature(s7, 5, rg, rg, i, t, t));
2325   Xen_define_typed_procedure(S_forget_region,          g_forget_region_w,          1, 0, 0, H_forget_region,   s7_make_signature(s7, 2, rg, rg));
2326   Xen_define_typed_procedure(S_make_region,            g_make_region_w,            0, 4, 0, H_make_region,     s7_make_signature(s7, 5, rg, i, i, t, t));
2327   Xen_define_typed_procedure(S_mix_region,             g_mix_region_w,             1, 4, 0, H_mix_region,      s7_make_signature(s7, 6, t, rg, i, t, t, t));
2328 
2329   Xen_define_typed_procedure(S_regions,                g_regions_w,                0, 0, 0, H_regions,         s7_make_signature(s7, 1, p));
2330   Xen_define_typed_procedure(S_region_framples,        g_region_framples_w,        1, 1, 0, H_region_framples, s7_make_signature(s7, 3, i, rg, i));
2331   Xen_define_typed_procedure(S_region_position,        g_region_position_w,        1, 1, 0, H_region_position, s7_make_signature(s7, 3, i, rg, i));
2332   Xen_define_typed_procedure(S_region_srate,           g_region_srate_w,           1, 0, 0, H_region_srate,    s7_make_signature(s7, 2, i, rg));
2333   Xen_define_typed_procedure(S_region_chans,           g_region_chans_w,           1, 0, 0, H_region_chans,    s7_make_signature(s7, 2, i, rg));
2334   Xen_define_typed_procedure(S_region_home,            g_region_home_w,            1, 0, 0, H_region_home,     s7_make_signature(s7, 2, p, rg));
2335   Xen_define_typed_procedure(S_region_maxamp,          g_region_maxamp_w,          1, 0, 0, H_region_maxamp,   s7_make_signature(s7, 2, f, rg));
2336   Xen_define_typed_procedure(S_region_maxamp_position, g_region_maxamp_position_w, 1, 0, 0, H_region_maxamp_position, s7_make_signature(s7, 2, i, rg));
2337   Xen_define_typed_procedure(S_region_sample,          g_region_sample_w,          2, 1, 0, H_region_sample,   s7_make_signature(s7, 4, f, rg, i, i));
2338   Xen_define_typed_procedure(S_region_to_vct,          g_region_to_vct_w,          1, 4, 0, H_region_to_vct,   s7_make_signature(s7, 6, fv, rg, i, i, i, fv));
2339   Xen_define_typed_procedure(S_is_region,              g_is_region_w,              1, 0, 0, H_is_region,       s7_make_signature(s7, 2, b, t));
2340 
2341   Xen_define_typed_procedure(S_integer_to_region,      g_integer_to_region_w,      1, 0, 0, H_integer_to_region, s7_make_signature(s7, 2, rg, i));
2342   Xen_define_typed_procedure(S_region_to_integer,      g_region_to_integer_w,      1, 0, 0, H_region_to_integer, s7_make_signature(s7, 2, i, rg));
2343 
2344   Xen_define_typed_dilambda(S_max_regions, g_max_regions_w, H_max_regions, S_set S_max_regions, g_set_max_regions_w, 0, 0, 1, 0,
2345 			    s7_make_signature(s7, 1, i), s7_make_signature(s7, 2, i, i));
2346 
2347   Xen_define_typed_dilambda(S_region_graph_style, g_region_graph_style_w, H_region_graph_style,
2348 			    S_set S_region_graph_style, g_set_region_graph_style_w,  0, 0, 1, 0,
2349 			    s7_make_signature(s7, 1, i), s7_make_signature(s7, 2, i, i));
2350 
2351 #if HAVE_SCHEME
2352   s7_set_setter(s7, ss->max_regions_symbol, s7_make_function(s7, "[acc-" S_max_regions "]", acc_max_regions, 2, 0, false, "accessor"));
2353   s7_set_setter(s7, ss->region_graph_style_symbol, s7_make_function(s7, "[acc-" S_region_graph_style "]", acc_region_graph_style, 2, 0, false, "accessor"));
2354 
2355   s7_set_documentation(s7, ss->max_regions_symbol, "*max-regions*: max number of regions saved on the region list");
2356   s7_set_documentation(s7, ss->region_graph_style_symbol, "*region-graph-style*: graph style of the region dialog graph (graph-lines etc)");
2357 
2358   s7_define_safe_function_star(s7, S_save_region, g_save_region, "region file sample-type header-type comment", H_save_region);
2359 #else
2360   Xen_define_typed_procedure(S_save_region,            g_save_region_w,            2, 7, 0, H_save_region,     s7_make_circular_signature(s7, 0, 1, t));
2361 #endif
2362 }
2363 
2364