1 #include "snd.h"
2 
3 static Xen save_hook;
4 
dont_save(snd_info * sp,const char * newname)5 static bool dont_save(snd_info *sp, const char *newname)
6 {
7   Xen res = Xen_false;
8   if (Xen_hook_has_list(save_hook))
9     res = run_or_hook(save_hook,
10 		      Xen_list_2(C_int_to_Xen_sound(sp->index),
11 				 (newname) ? C_string_to_Xen_string(newname) : Xen_false),
12 		      S_save_hook);
13   return(Xen_is_true(res));
14 }
15 
16 
after_edit(chan_info * cp)17 void after_edit(chan_info *cp)
18 {
19   reflect_edit_history_change(cp);
20   reflect_enved_fft_change(cp);
21   if ((cp->hookable == WITH_HOOK) &&
22       (Xen_is_hook(cp->after_edit_hook)) &&
23       (Xen_hook_has_list(cp->after_edit_hook)))
24     run_hook(cp->after_edit_hook, Xen_empty_list, S_after_edit_hook);
25 }
26 
27 
free_sound_list(chan_info * cp)28 void free_sound_list(chan_info *cp)
29 {
30   if (cp)
31     {
32       if (cp->sounds)
33 	{
34 	  int i;
35 	  if ((cp->sound) &&
36 	      (cp->sound->playing))
37 	    stop_playing_sound_without_hook(cp->sound, PLAY_CLOSE);
38 	  for (i = 0; i < cp->sound_size; i++)
39 	    if (cp->sounds[i])
40 	      cp->sounds[i] = free_snd_data(cp->sounds[i]);
41 	  free(cp->sounds);
42 	  cp->sounds = NULL;
43 	}
44       cp->sound_ctr = NOT_A_SOUND;
45       cp->sound_size = 0;
46     }
47 }
48 
49 
release_pending_sounds(chan_info * cp,int edit_ctr)50 static void release_pending_sounds(chan_info *cp, int edit_ctr)
51 {
52   /* look for buffers or temp files that are no longer reachable after pruning the edit tree */
53   if ((cp) && (cp->sounds))
54     {
55       int i;
56       if ((cp->sound) &&
57 	  (cp->sound->playing))
58 	stop_playing_sound_without_hook(cp->sound, PLAY_CLOSE);
59       for (i = 0; i < cp->sound_size; i++)
60 	{
61 	  snd_data *sd;
62 	  sd = cp->sounds[i];
63 	  if (sd)
64 	    {
65 	      if (sd->edit_ctr >= edit_ctr)
66 		cp->sounds[i] = free_snd_data(sd);
67 	      else cp->sound_ctr = i;
68 	    }
69 	}
70     }
71 }
72 
73 
74 #define EDIT_ALLOC_SIZE 32
75 /* EDIT_ALLOC_SIZE is the allocation amount (pointers) each time cp->sounds is (re)allocated */
76 
prepare_sound_list(chan_info * cp)77 void prepare_sound_list(chan_info *cp)
78 {
79   cp->sound_ctr++;
80   /* this is the only place the sound set is incremented */
81   if (cp->sound_ctr >= cp->sound_size)
82     {
83       int i;
84       cp->sound_size += EDIT_ALLOC_SIZE;
85       cp->sounds = (snd_data **)realloc(cp->sounds, cp->sound_size * sizeof(snd_data *));
86       for (i = cp->sound_ctr; i < cp->sound_size; i++) cp->sounds[i] = NULL;
87     }
88   if (cp->sounds[cp->sound_ctr])
89     {
90       if ((cp->sound) && (cp->sound->playing)) stop_playing_sound_without_hook(cp->sound, PLAY_CLOSE);
91       cp->sounds[cp->sound_ctr] = free_snd_data(cp->sounds[cp->sound_ctr]);
92     }
93 }
94 
95 
add_sound_file_to_edit_list(chan_info * cp,const char * name,snd_io * io,file_info * hdr,file_delete_t temp,int chan)96 static int add_sound_file_to_edit_list(chan_info *cp, const char *name, snd_io *io, file_info *hdr, file_delete_t temp, int chan)
97 {
98   prepare_sound_list(cp);
99   cp->sounds[cp->sound_ctr] = make_snd_data_file(name, io, hdr, temp, cp->edit_ctr, chan);
100   return(cp->sound_ctr);
101 }
102 
103 
104 static ed_list *free_ed_list(ed_list *ed, chan_info *cp);
105 
prune_edits(chan_info * cp,int edpt)106 static void prune_edits(chan_info *cp, int edpt)
107 {
108   if (cp->edits[edpt])
109     {
110       int i;
111       if (ss->deferred_regions > 0)
112 	sequester_deferred_regions(cp, edpt - 1);
113       for (i = edpt; i < cp->edit_size; i++)
114 	cp->edits[i] = free_ed_list(cp->edits[i], cp);
115       release_pending_sounds(cp, edpt);
116     }
117 }
118 
119 
is_editable(chan_info * cp)120 bool is_editable(chan_info *cp)
121 {
122   if (!(cp->editable)) return(false);
123   if ((Xen_is_hook(cp->edit_hook)) &&
124       (Xen_hook_has_list(cp->edit_hook)))
125     {
126       Xen res;
127       res = run_or_hook(cp->edit_hook, Xen_empty_list, S_edit_hook);
128       if (Xen_is_true(res))
129 	return(false);
130     }
131   return(true);
132 }
133 
134 
increment_edit_ctr(chan_info * cp)135 static void increment_edit_ctr(chan_info *cp)
136 {
137   cp->edit_ctr++;
138   if (cp->edit_ctr >= cp->edit_size)
139     {
140       int i;
141       cp->edit_size += EDIT_ALLOC_SIZE;
142       if (!cp->edits) cp->edits = (ed_list **)calloc(cp->edit_size, sizeof(ed_list *));
143       else cp->edits = (ed_list **)realloc(cp->edits, cp->edit_size * sizeof(ed_list *));
144       for (i = cp->edit_ctr; i < cp->edit_size; i++) cp->edits[i] = NULL;
145     }
146 }
147 
148 
prepare_edit_list(chan_info * cp,int pos,const char * caller)149 static bool prepare_edit_list(chan_info *cp, int pos, const char *caller)
150 {
151   /* pos is the edit position the current edit is referring to --
152    *   we normally can't set up an edit list entry that refers to a situation
153    *   that will be clobbered by prune_edits below
154    */
155   snd_info *sp;
156 
157   if (!(is_editable(cp))) return(false); /* this may represent a second call on edit-hook for this edit */
158   if (pos > cp->edit_ctr)
159     {
160       Xen_error(Xen_make_error_type("no-such-edit"),
161 		Xen_list_6(C_string_to_Xen_string("~A: edpos: ~A but ~A chan ~A has ~A edits"),
162 			   C_string_to_Xen_string(caller),
163 			   C_int_to_Xen_integer(pos),
164 			   C_string_to_Xen_string(cp->sound->short_filename),
165 			   C_int_to_Xen_integer(cp->chan),
166 			   C_int_to_Xen_integer(cp->edit_ctr)));
167     }
168   sp = cp->sound;
169   stop_peak_env(cp);
170   if ((sp) && (sp->playing)) stop_playing_sound_without_hook(sp, PLAY_EDIT);
171   increment_edit_ctr(cp);
172   prune_edits(cp, cp->edit_ctr);
173   return(true);
174 }
175 
176 
reflect_sample_change_in_axis(chan_info * cp)177 static void reflect_sample_change_in_axis(chan_info *cp)
178 {
179   axis_info *ap;
180   ap = cp->axis;
181   if (ap)
182     {
183       mus_long_t samps;
184       samps = current_samples(cp);
185       ap->xmax = (double)samps / (double)snd_srate(cp->sound);
186       ap->x_ambit = ap->xmax - ap->xmin;
187       if (ap->x1 > ap->xmax) ap->x1 = ap->xmax;
188       if ((samps == 0) || (ap->no_data))
189 	{
190 	  ap->no_data = (samps == 0);
191 	  if (ap->xlabel) free(ap->xlabel);
192 	  if (samps == 0)
193 	    ap->xlabel = mus_strdup("(no data)");
194 	  else
195 	    {
196 	      if (ap->default_xlabel)
197 		ap->xlabel = mus_strdup(ap->default_xlabel);
198 	      else ap->xlabel = mus_strdup("time");
199 	    }
200 	}
201       set_x_bounds(ap);
202     }
203 }
204 
205 
reflect_file_change_in_label(chan_info * cp)206 void reflect_file_change_in_label(chan_info *cp)
207 {
208 #if (!USE_NO_GUI)
209   snd_info *sp;
210 
211   if (cp->edit_ctr == 0) return;
212   sp = cp->sound;
213 
214   if (!(cp->squelch_update))
215     {
216       char *starred_name;
217       int len;
218 
219       len = strlen(shortname(sp)) + 16;
220       starred_name = (char *)calloc(len, sizeof(char));
221       strcopy(starred_name, shortname_indexed(sp), len);
222 
223       if ((sp->user_read_only == FILE_READ_ONLY) ||
224 	  (sp->file_read_only == FILE_READ_ONLY))
225 	strcat(starred_name, "(*)");
226       else strcat(starred_name, "*");
227       set_sound_pane_file_label(sp, starred_name);
228       free(starred_name);
229     }
230 #endif
231 }
232 
233 
check_for_first_edit(chan_info * cp)234 static void check_for_first_edit(chan_info *cp)
235 {
236   if (cp->edit_ctr == 1) /* first edit on this file (?) */
237     reflect_file_change_in_label(cp);
238 }
239 
240 
241 static Xen save_state_hook;
242 
run_save_state_hook(const char * file)243 char *run_save_state_hook(const char *file)
244 {
245   char *filename;
246   filename = mus_strdup(file);
247   if (Xen_hook_has_list(save_state_hook))
248     {
249       Xen result;
250 #if HAVE_SCHEME
251       result = s7_call(s7, save_state_hook, s7_cons(s7, C_string_to_Xen_string(filename), s7_nil(s7)));
252       if (Xen_is_string(result))
253 	{
254 	  free(filename);
255 	  filename = mus_strdup(Xen_string_to_C_string(result));
256 	}
257 #else
258       Xen procs, fname;
259       fname = C_string_to_Xen_string(filename);
260       procs = Xen_hook_list(save_state_hook);
261       while (!Xen_is_null(procs))
262 	{
263 	  result = Xen_call_with_1_arg(Xen_car(procs), fname, "save state hook");
264 	  if (Xen_is_string(result))
265 	    {
266 	      free(filename);
267 	      filename = mus_strdup(Xen_string_to_C_string(result));
268 	    }
269 	  procs = Xen_cdr(procs);
270 	}
271 #endif
272     }
273   return(filename);
274 }
275 
276 
277 
278 /* -------- EDIT LISTS --------
279  *
280  * each channel has a list of lists containing the current edit history and the associated sound temp files or buffers
281  * undo: back up current list position
282  * redo: push position forward
283  * No actual changes are flushed out to the file system until the file is saved.
284  *
285  * the editing possibilities are insert, change, delete, scale, zero, env, mix
286  * All input goes through these lists (with minor exceptions -- see chn_sample below).
287  *
288  * The accessors are highly optimized (split into numerous choices) since everything else in Snd
289  *   goes through the accessors to get at the data.
290  *
291  * I tried a flattened version (using ed_fragment* rather than ed_fragment**) which involves fewer calls on malloc,
292  *   but the edit list is a "real" list -- we do internal insertions and deletions and so on, so it's simpler
293  *   to use in the current form.
294  */
295 
296 
297 /* fragment ramp info */
298 
299 typedef struct {
300   double start, incr;
301 } ramp_state;
302 
303 typedef struct {
304   double start, incr, scl, off;
305 } xramp_state;
306 
307 typedef struct {
308   int ramps, xramps;
309   ramp_state *ramp_list;
310   xramp_state *xramp_list;
311 } ed_ramps;
312 
313 
314 /* fragment mix info */
315 
316 typedef struct {
317   int size;                              /* size of mix_list, but some entries can be empty */
318   mix_state **mix_list;
319 } ed_mixes;
320 
321 
322 /* ed list fragment */
323 
324 typedef struct ed_fragment {             /* this name is necessary even in straight C */
325   int typ,                               /* code for accessor choice (ED_SIMPLE etc) */
326       snd;                               /* either an index into the cp->sounds array (snd_data structs) or EDIT_LIST_END|ZERO_MARK */
327   mus_long_t out,                               /* running segment location within current overall edited data */
328         beg,                               /* index into the associated data => start point of data used in current segment */
329         end;                               /* index into the associated data => end point of data used in current segment */
330   mus_float_t scl;                               /* segment scaler */
331   ed_ramps *ramps;
332   ed_mixes *mixes;
333   struct ed_fragment *next;
334 } ed_fragment;
335 
336 
337 
338 /* reader ramp info */
339 
340 typedef struct {
341   int ramps, xramps;
342   double *incrs, *vals;
343   double *xincrs, *xvals;
344   mus_float_t (*rampf)(struct snd_fd *sf);
345   mus_float_t (*rev_rampf)(struct snd_fd *sf);
346 } reader_ramps;
347 
348 
349 /* reader mix info */
350 
351 typedef struct {
352   short size;
353   snd_fd **sfs;
354 } reader_mixes;
355 
356 
357 
358 /* two ed_fragment->snd markers */
359 
360 #define EDIT_LIST_END_MARK -2
361 #define EDIT_LIST_ZERO_MARK -1
362 
363 #define FRAGMENTS(Ed)                         (ed_fragment **)((Ed)->fragments)
364 #define FRAGMENT(Ed, Pos)                    ((ed_fragment **)((Ed)->fragments))[Pos]
365 
366 /* #define FRAGMENT_RAMPS(Ed, Pos)              ((ed_fragment **)((Ed)->fragments))[Pos]->ramps */
367 /* #define FRAGMENT_RAMP_LIST(Ed, Pos)          ((ed_fragment **)((Ed)->fragments))[Pos]->ramps->ramp_list */
368 #define FRAGMENT_RAMP_LIST_SIZE(Ed, Pos)     ((ed_fragment **)((Ed)->fragments))[Pos]->ramps->ramps
369 /* #define FRAGMENT_XRAMP_LIST(Ed, Pos)         ((ed_fragment **)((Ed)->fragments))[Pos]->ramps->xramp_list */
370 #define FRAGMENT_XRAMP_LIST_SIZE(Ed, Pos)    ((ed_fragment **)((Ed)->fragments))[Pos]->ramps->xramps
371 
372 #define FRAGMENT_MIXES(Ed, Pos)              ((ed_fragment **)((Ed)->fragments))[Pos]->mixes
373 #define FRAGMENT_MIX_LIST_SIZE(Ed, Pos)      ((ed_fragment **)((Ed)->fragments))[Pos]->mixes->size
374 #define FRAGMENT_MIX_LIST(Ed, Pos)           ((ed_fragment **)((Ed)->fragments))[Pos]->mixes->mix_list
375 
376 #define FRAGMENT_GLOBAL_POSITION(Ed, Pos)    ((ed_fragment **)((Ed)->fragments))[Pos]->out
377 #define FRAGMENT_LOCAL_POSITION(Ed, Pos)     ((ed_fragment **)((Ed)->fragments))[Pos]->beg
378 #define FRAGMENT_LOCAL_END(Ed, Pos)          ((ed_fragment **)((Ed)->fragments))[Pos]->end
379 #define FRAGMENT_SCALER(Ed, Pos)             ((ed_fragment **)((Ed)->fragments))[Pos]->scl
380 #define FRAGMENT_TYPE(Ed, Pos)               ((ed_fragment **)((Ed)->fragments))[Pos]->typ
381 #define FRAGMENT_SOUND(Ed, Pos)              ((ed_fragment **)((Ed)->fragments))[Pos]->snd
382 #define FRAGMENT_LENGTH(Ed, Pos)             (FRAGMENT_LOCAL_END(Ed, Pos) - FRAGMENT_LOCAL_POSITION(Ed, Pos) + 1)
383 
384 #define FRAGMENT_RAMP_START(Ed, Pos, Rmp)    ((ed_fragment **)((Ed)->fragments))[Pos]->ramps->ramp_list[Rmp].start
385 #define FRAGMENT_RAMP_INCR(Ed, Pos, Rmp)     ((ed_fragment **)((Ed)->fragments))[Pos]->ramps->ramp_list[Rmp].incr
386 #define FRAGMENT_XRAMP_START(Ed, Pos, Rmp)   ((ed_fragment **)((Ed)->fragments))[Pos]->ramps->xramp_list[Rmp].start
387 #define FRAGMENT_XRAMP_INCR(Ed, Pos, Rmp)    ((ed_fragment **)((Ed)->fragments))[Pos]->ramps->xramp_list[Rmp].incr
388 #define FRAGMENT_XRAMP_SCALER(Ed, Pos, Rmp)  ((ed_fragment **)((Ed)->fragments))[Pos]->ramps->xramp_list[Rmp].scl
389 #define FRAGMENT_XRAMP_OFFSET(Ed, Pos, Rmp)  ((ed_fragment **)((Ed)->fragments))[Pos]->ramps->xramp_list[Rmp].off
390 
391 #define FRAGMENT_MIX_STATE(Ed, Pos, Mix)     ((ed_fragment **)((Ed)->fragments))[Pos]->mixes->mix_list[Mix]
392 /* #define FRAGMENT_MIX_INDEX(Ed, Pos, Mix)     ((ed_fragment **)((Ed)->fragments))[Pos]->mixes->mix_list[Mix]->index */
393 /* #define FRAGMENT_MIX_LENGTH(Ed, Pos, Mix)    ((ed_fragment **)((Ed)->fragments))[Pos]->mixes->mix_list[Mix]->len */
394 /* #define FRAGMENT_MIX_SCALER(Ed, Pos, Mix)    ((ed_fragment **)((Ed)->fragments))[Pos]->mixes->mix_list[Mix]->scaler */
395 /* #define FRAGMENT_MIX_BEG(Ed, Pos, Mix)       ((ed_fragment **)((Ed)->fragments))[Pos]->mixes->mix_list[Mix]->beg */
396 
397 
398 #define ED_GLOBAL_POSITION(Ed)               (Ed)->out
399 #define ED_LOCAL_POSITION(Ed)                (Ed)->beg
400 #define ED_LOCAL_END(Ed)                     (Ed)->end
401 #define ED_SCALER(Ed)                        (Ed)->scl
402 #define ED_TYPE(Ed)                          (Ed)->typ
403 #define ED_SOUND(Ed)                         (Ed)->snd
404 
405 #define ED_RAMPS(Ed)                         (Ed)->ramps
406 #define ED_RAMP_LIST(Ed)                     (Ed)->ramps->ramp_list
407 #define ED_RAMP_LIST_SIZE(Ed)                (Ed)->ramps->ramps
408 #define ED_XRAMP_LIST(Ed)                    (Ed)->ramps->xramp_list
409 #define ED_XRAMP_LIST_SIZE(Ed)               (Ed)->ramps->xramps
410 
411 #define ED_RAMP_START(Ed, Rmp)               (Ed)->ramps->ramp_list[Rmp].start
412 #define ED_RAMP_INCR(Ed, Rmp)                (Ed)->ramps->ramp_list[Rmp].incr
413 #define ED_XRAMP_START(Ed, Rmp)              (Ed)->ramps->xramp_list[Rmp].start
414 #define ED_XRAMP_INCR(Ed, Rmp)               (Ed)->ramps->xramp_list[Rmp].incr
415 #define ED_XRAMP_SCALER(Ed, Rmp)             (Ed)->ramps->xramp_list[Rmp].scl
416 #define ED_XRAMP_OFFSET(Ed, Rmp)             (Ed)->ramps->xramp_list[Rmp].off
417 
418 #define ED_MIXES(Ed)                         (Ed)->mixes
419 #define ED_MIX_LIST(Ed)                      (Ed)->mixes->mix_list
420 /* #define ED_MIX_LIST_SIZE(Ed)                 (Ed)->mixes->size */
421 /* #define ED_MIX_STATE(Ed, Mix)                (Ed)->mixes->mix_list[Mix] */
422 /* #define ED_MIX_INDEX(Ed, Mix)                (Ed)->mixes->mix_list[Mix]->index */
423 /* #define ED_MIX_LENGTH(Ed, Mix)               (Ed)->mixes->mix_list[Mix]->len */
424 /* #define ED_MIX_SCALER(Ed, Mix)               (Ed)->mixes->mix_list[Mix]->scaler */
425 /* #define ED_MIX_BEG(Ed, Mix)                  (Ed)->mixes->mix_list[Mix]->beg */
426 
427 #define MIX_LIST_SCALER(Ed, Mix)             (Ed)->mix_list[Mix]->scaler
428 #define MIX_LIST_STATE(Ed, Mix)              (Ed)->mix_list[Mix]
429 #define MIX_LIST_INDEX(Ed, Mix)              (Ed)->mix_list[Mix]->index
430 /* #define MIX_LIST_LENGTH(Ed, Mix)             (Ed)->mix_list[Mix]->len */
431 #define MIX_LIST_BEG(Ed, Mix)                (Ed)->mix_list[Mix]->beg
432 
433 
434 #define READER_GLOBAL_POSITION(Sf)           ((ed_fragment *)((Sf)->cb))->out
435 #define READER_LOCAL_POSITION(Sf)            ((ed_fragment *)((Sf)->cb))->beg
436 #define READER_LOCAL_END(Sf)                 ((ed_fragment *)((Sf)->cb))->end
437 #define READER_SCALER(Sf)                    ((ed_fragment *)((Sf)->cb))->scl
438 #define READER_TYPE(Sf)                      ((ed_fragment *)((Sf)->cb))->typ
439 #define READER_SOUND(Sf)                     ((ed_fragment *)((Sf)->cb))->snd
440 
441 #define READER_RAMP_START(Sf, Pos)           ((ed_fragment *)((Sf)->cb))->ramps->ramp_list[Pos].start
442 #define READER_RAMP_INCR(Sf, Pos)            ((ed_fragment *)((Sf)->cb))->ramps->ramp_list[Pos].incr
443 #define READER_XRAMP_START(Sf, Pos)          ((ed_fragment *)((Sf)->cb))->ramps->xramp_list[Pos].start
444 #define READER_XRAMP_INCR(Sf, Pos)           ((ed_fragment *)((Sf)->cb))->ramps->xramp_list[Pos].incr
445 #define READER_XRAMP_SCALER(Sf, Pos)         ((ed_fragment *)((Sf)->cb))->ramps->xramp_list[Pos].scl
446 #define READER_XRAMP_OFFSET(Sf, Pos)         ((ed_fragment *)((Sf)->cb))->ramps->xramp_list[Pos].off
447 
448 #define READER_RAMPS(Sf)                     ((reader_ramps *)((Sf)->ramps))->ramps
449 #define READER_XRAMPS(Sf)                    ((reader_ramps *)((Sf)->ramps))->xramps
450 #define READER_INCRS(Sf)                     ((reader_ramps *)((Sf)->ramps))->incrs
451 #define READER_VALS(Sf)                      ((reader_ramps *)((Sf)->ramps))->vals
452 #define READER_XINCRS(Sf)                    ((reader_ramps *)((Sf)->ramps))->xincrs
453 #define READER_XVALS(Sf)                     ((reader_ramps *)((Sf)->ramps))->xvals
454 
455 #define READER_INCR(Sf, Pos)                 ((reader_ramps *)((Sf)->ramps))->incrs[Pos]
456 #define READER_VAL(Sf, Pos)                  ((reader_ramps *)((Sf)->ramps))->vals[Pos]
457 #define READER_XINCR(Sf, Pos)                ((reader_ramps *)((Sf)->ramps))->xincrs[Pos]
458 #define READER_XVAL(Sf, Pos)                 ((reader_ramps *)((Sf)->ramps))->xvals[Pos]
459 #define READER_RAMPF(Sf)                     ((reader_ramps *)((Sf)->ramps))->rampf
460 #define READER_REV_RAMPF(Sf)                 ((reader_ramps *)((Sf)->ramps))->rev_rampf
461 
462 /* #define READER_MIXES(Sf)                     ((ed_fragment *)((Sf)->cb))->mixes */
463 #define READER_MIX_LIST_SIZE(Sf)             ((ed_fragment *)((Sf)->cb))->mixes->size
464 /* #define READER_MIX_LIST(Sf)                  ((ed_fragment *)((Sf)->cb))->mixes->mix_list */
465 #define READER_MIX_STATE(Sf, Mix)            ((ed_fragment *)((Sf)->cb))->mixes->mix_list[Mix]
466 #define READER_MIX_INDEX(Sf, Mix)            ((ed_fragment *)((Sf)->cb))->mixes->mix_list[Mix]->index
467 #define READER_MIX_LENGTH(Sf, Mix)           ((ed_fragment *)((Sf)->cb))->mixes->mix_list[Mix]->len
468 #define READER_MIX_SCALER(Sf, Mix)           ((ed_fragment *)((Sf)->cb))->mixes->mix_list[Mix]->scaler
469 #define READER_MIX_BEG(Sf, Mix)              ((ed_fragment *)((Sf)->cb))->mixes->mix_list[Mix]->beg
470 
471 
472 
473 /* -------------------------------- fragment accessors -------------------------------- */
474 
next_ramp1_value(snd_fd * sf)475 static mus_float_t next_ramp1_value(snd_fd *sf)
476 {
477   mus_float_t val;
478   val = READER_VAL(sf, 0);
479   READER_VAL(sf, 0) += READER_INCR(sf, 0);
480   return(val);
481 }
482 
previous_ramp1_value(snd_fd * sf)483 static mus_float_t previous_ramp1_value(snd_fd *sf)
484 {
485   mus_float_t val;
486   val = READER_VAL(sf, 0);
487   READER_VAL(sf, 0) -= READER_INCR(sf, 0);
488   return(val);
489 }
490 
491 
next_ramp2_value(snd_fd * sf)492 static mus_float_t next_ramp2_value(snd_fd *sf)
493 {
494   mus_float_t val;
495   val = READER_VAL(sf, 0) * READER_VAL(sf, 1);
496   READER_VAL(sf, 0) += READER_INCR(sf, 0);
497   READER_VAL(sf, 1) += READER_INCR(sf, 1);
498   return(val);
499 }
500 
previous_ramp2_value(snd_fd * sf)501 static mus_float_t previous_ramp2_value(snd_fd *sf)
502 {
503   mus_float_t val;
504   val = READER_VAL(sf, 0) * READER_VAL(sf, 1);
505   READER_VAL(sf, 0) -= READER_INCR(sf, 0);
506   READER_VAL(sf, 1) -= READER_INCR(sf, 1);
507   return(val);
508 }
509 
510 
next_ramp_value(snd_fd * sf)511 static mus_float_t next_ramp_value(snd_fd *sf)
512 {
513   mus_float_t val;
514   int i;
515   val = READER_VAL(sf, 0);
516   READER_VAL(sf, 0) += READER_INCR(sf, 0);
517   for (i = 1; i < READER_RAMPS(sf); i++)
518     {
519       val *= READER_VAL(sf, i);
520       READER_VAL(sf, i) += READER_INCR(sf, i);
521     }
522   return(val);
523 }
524 
previous_ramp_value(snd_fd * sf)525 static mus_float_t previous_ramp_value(snd_fd *sf)
526 {
527   mus_float_t val;
528   int i;
529   val = READER_VAL(sf, 0);
530   READER_VAL(sf, 0) -= READER_INCR(sf, 0);
531   for (i = 1; i < READER_RAMPS(sf); i++)
532     {
533       val *= READER_VAL(sf, i);
534       READER_VAL(sf, i) -= READER_INCR(sf, i);
535     }
536   return(val);
537 }
538 
539 
next_xramp1_value(snd_fd * sf)540 static mus_float_t next_xramp1_value(snd_fd *sf)
541 {
542   mus_float_t val;
543   val = (READER_XRAMP_OFFSET(sf, 0) + (READER_XRAMP_SCALER(sf, 0) * READER_XVAL(sf, 0)));
544   READER_XVAL(sf, 0) *= READER_XINCR(sf, 0);
545   return(val);
546 }
547 
548 /* ideally we'd invert the increment if reading an xramp in reverse, making this a multiply, not a divide,
549  *   but then we'd need a fixup when the reader changes direction -- probably not worth the complexity.
550  */
551 
previous_xramp1_value(snd_fd * sf)552 static mus_float_t previous_xramp1_value(snd_fd *sf)
553 {
554   mus_float_t val;
555   val = (READER_XRAMP_OFFSET(sf, 0) + (READER_XRAMP_SCALER(sf, 0) * READER_XVAL(sf, 0)));
556   READER_XVAL(sf, 0) /= READER_XINCR(sf, 0);
557   return(val);
558 }
559 
560 
next_xramp_value(snd_fd * sf)561 static mus_float_t next_xramp_value(snd_fd *sf)
562 {
563   int i;
564   mus_float_t val;
565   val = (READER_XRAMP_OFFSET(sf, 0) + (READER_XRAMP_SCALER(sf, 0) * READER_XVAL(sf, 0)));
566   READER_XVAL(sf, 0) *= READER_XINCR(sf, 0);
567   for (i = 1; i < READER_XRAMPS(sf); i++)
568     {
569       val *= (READER_XRAMP_OFFSET(sf, i) + (READER_XRAMP_SCALER(sf, i) * READER_XVAL(sf, i)));
570       READER_XVAL(sf, i) *= READER_XINCR(sf, i);
571     }
572   return(val);
573 }
574 
previous_xramp_value(snd_fd * sf)575 static mus_float_t previous_xramp_value(snd_fd *sf)
576 {
577   int i;
578   mus_float_t val = 1.0;
579   for (i = 0; i < READER_XRAMPS(sf); i++)
580     {
581       val *= (READER_XRAMP_OFFSET(sf, i) + (READER_XRAMP_SCALER(sf, i) * READER_XVAL(sf, i)));
582       READER_XVAL(sf, i) /= READER_XINCR(sf, i);
583     }
584   return(val);
585 }
586 
587 
next_xramp_ramp_value(snd_fd * sf)588 static mus_float_t next_xramp_ramp_value(snd_fd *sf)
589 {
590   return(next_xramp_value(sf) * next_ramp_value(sf));
591 }
592 
previous_xramp_ramp_value(snd_fd * sf)593 static mus_float_t previous_xramp_ramp_value(snd_fd *sf)
594 {
595   return(previous_xramp_value(sf) * previous_ramp_value(sf));
596 }
597 
598 
599 static mus_float_t previous_sound(snd_fd *sf);
600 mus_float_t next_sound(snd_fd *sf);
601 
602 
end_sample_value(snd_fd * ignore)603 static mus_float_t end_sample_value(snd_fd *ignore) {return(0.0);}
604 
605 
next_sample_value(snd_fd * sf)606 static mus_float_t next_sample_value(snd_fd *sf)
607 {
608   if (sf->loc > sf->last)
609     return(next_sound(sf));
610   else return(sf->data[sf->loc++] * sf->fscaler);
611 }
612 
613 
next_sample_value_unchecked(snd_fd * sf)614 static mus_float_t next_sample_value_unchecked(snd_fd *sf)
615 {
616   return(sf->data[sf->loc++] * sf->fscaler);
617 }
618 
previous_sample_value(snd_fd * sf)619 static mus_float_t previous_sample_value(snd_fd *sf)
620 {
621   if (sf->loc < sf->first)
622     return(previous_sound(sf));
623   else return(sf->data[sf->loc--] * sf->fscaler);
624 }
625 
626 mus_float_t previous_sample_value_unchecked(snd_fd *sf) ;
previous_sample_value_unchecked(snd_fd * sf)627 mus_float_t previous_sample_value_unchecked(snd_fd *sf)
628 {
629   return(sf->data[sf->loc--] * sf->fscaler);
630 }
631 
632 
633 mus_float_t next_sample_value_unscaled(snd_fd *sf);
next_sample_value_unscaled(snd_fd * sf)634 mus_float_t next_sample_value_unscaled(snd_fd *sf)
635 {
636   if (sf->loc > sf->last)
637     return(next_sound(sf));
638   else return(sf->data[sf->loc++]);
639 }
640 
641 mus_float_t next_sample_value_unscaled_and_unchecked(snd_fd *sf);
next_sample_value_unscaled_and_unchecked(snd_fd * sf)642 mus_float_t next_sample_value_unscaled_and_unchecked(snd_fd *sf)
643 {
644   return(sf->data[sf->loc++]);
645 }
646 
647 mus_float_t previous_sample_value_unscaled(snd_fd *sf) ;
previous_sample_value_unscaled(snd_fd * sf)648 mus_float_t previous_sample_value_unscaled(snd_fd *sf)
649 {
650   if (sf->loc < sf->first)
651     return(previous_sound(sf));
652   else return(sf->data[sf->loc--]);
653 }
654 
655 
656 mus_float_t previous_sample_value_unscaled_and_unchecked(snd_fd *sf);
previous_sample_value_unscaled_and_unchecked(snd_fd * sf)657 mus_float_t previous_sample_value_unscaled_and_unchecked(snd_fd *sf)
658 {
659   return(sf->data[sf->loc--]);
660 }
661 
662 
next_zero(snd_fd * sf)663 static mus_float_t next_zero(snd_fd *sf)
664 {
665   if (sf->loc > sf->last) return(next_sound(sf));
666   sf->loc++;
667   return(0.0);
668 }
669 
previous_zero(snd_fd * sf)670 static mus_float_t previous_zero(snd_fd *sf)
671 {
672   if (sf->loc < sf->first) return(previous_sound(sf));
673   sf->loc--;
674   return(0.0);
675 }
676 
677 
next_ramp1(snd_fd * sf)678 static mus_float_t next_ramp1(snd_fd *sf)
679 {
680   if (sf->loc > sf->last)
681      return(next_sound(sf));
682   else
683     {
684       mus_float_t val;
685       val = sf->data[sf->loc++] * READER_VAL(sf, 0);
686       READER_VAL(sf, 0) += READER_INCR(sf, 0);
687       return(val);
688     }
689 }
690 
next_ramp1_unchecked(snd_fd * sf)691 static mus_float_t next_ramp1_unchecked(snd_fd *sf)
692 {
693   mus_float_t val;
694   val = sf->data[sf->loc++] * READER_VAL(sf, 0);
695   READER_VAL(sf, 0) += READER_INCR(sf, 0);
696   return(val);
697 }
698 
previous_ramp1(snd_fd * sf)699 static mus_float_t previous_ramp1(snd_fd *sf)
700 {
701   if (sf->loc < sf->first)
702     return(previous_sound(sf));
703   else
704     {
705       mus_float_t val;
706       val = sf->data[sf->loc--] * READER_VAL(sf, 0);
707       READER_VAL(sf, 0) -= READER_INCR(sf, 0);
708       return(val);
709     }
710 }
711 
712 
next_ramp_f(snd_fd * sf)713 static mus_float_t next_ramp_f(snd_fd *sf)
714 {
715   if (sf->loc > sf->last)
716     return(next_sound(sf));
717   else return(sf->data[sf->loc++] * (*(READER_RAMPF(sf)))(sf));
718 }
719 
previous_ramp_f(snd_fd * sf)720 static mus_float_t previous_ramp_f(snd_fd *sf)
721 {
722   if (sf->loc < sf->first)
723     return(previous_sound(sf));
724   else return(sf->data[sf->loc--] * (*(READER_REV_RAMPF(sf)))(sf));
725 }
726 
727 
next_ramp(snd_fd * sf)728 static mus_float_t next_ramp(snd_fd *sf)
729 {
730   if (sf->loc > sf->last)
731     return(next_sound(sf));
732   else return(sf->data[sf->loc++] * next_ramp_value(sf));
733 }
734 
previous_ramp(snd_fd * sf)735 static mus_float_t previous_ramp(snd_fd *sf)
736 {
737   if (sf->loc < sf->first)
738     return(previous_sound(sf));
739   else return(sf->data[sf->loc--] * previous_ramp_value(sf));
740 }
741 
742 
next_xramp1(snd_fd * sf)743 static mus_float_t next_xramp1(snd_fd *sf)
744 {
745   if (sf->loc > sf->last)
746     return(next_sound(sf));
747   else return(sf->data[sf->loc++] * READER_SCALER(sf) * next_xramp1_value(sf));
748 }
749 
previous_xramp1(snd_fd * sf)750 static mus_float_t previous_xramp1(snd_fd *sf)
751 {
752   if (sf->loc < sf->first)
753     return(previous_sound(sf));
754   else return(sf->data[sf->loc--] * READER_SCALER(sf) * previous_xramp1_value(sf));
755 }
756 
757 
next_xramp(snd_fd * sf)758 static mus_float_t next_xramp(snd_fd *sf)
759 {
760   if (sf->loc > sf->last)
761     return(next_sound(sf));
762   else return(sf->data[sf->loc++] * READER_SCALER(sf) * next_xramp_value(sf));
763 }
764 
previous_xramp(snd_fd * sf)765 static mus_float_t previous_xramp(snd_fd *sf)
766 {
767   if (sf->loc < sf->first)
768     return(previous_sound(sf));
769   else return(sf->data[sf->loc--] * READER_SCALER(sf) * previous_xramp_value(sf));
770 }
771 
772 
773 
774 /* mix readers */
775 
read_mix_list_samples(snd_fd * sf)776 static mus_float_t read_mix_list_samples(snd_fd *sf)
777 {
778   reader_mixes *m;
779   int i;
780   mus_float_t sum;
781 
782   m = (reader_mixes *)(sf->mixes);
783   if (m->size == 0) return(0.0); /* this is apparently possible?? */
784 
785   sum = read_sample(m->sfs[0]);
786   for (i = 1; i < m->size; i++)
787     sum += read_sample(m->sfs[i]);
788   return(sum);
789 }
790 
791 
next_mix(snd_fd * sf)792 static mus_float_t next_mix(snd_fd *sf)
793 {
794   /* read_sample here would call runf => next_mix => infinite recursion */
795   if (sf->loc > sf->last) return(next_sound(sf)); /* next_sound here refers to whatever follows the mixed portion */
796   return((sf->data[sf->loc++] * sf->fscaler) + read_mix_list_samples(sf));
797 }
798 
799 /* if underlying data was scaled,and we added a mix, we couldn't share the existing scale,
800  *   and mixes can be added after subsequent scaling, so we're constrained to use the mix-amps
801  */
802 
previous_mix(snd_fd * sf)803 static mus_float_t previous_mix(snd_fd *sf)
804 {
805   if (sf->loc < sf->first) return(previous_sound(sf));
806   return((sf->data[sf->loc--] * sf->fscaler) + read_mix_list_samples(sf));
807 }
808 
809 
next_mix_zero(snd_fd * sf)810 static mus_float_t next_mix_zero(snd_fd *sf)
811 {
812   if (sf->loc > sf->last) return(next_sound(sf)); /* next_sound here refers to whatever follows the mixed portion */
813   sf->loc++;
814   return(read_mix_list_samples(sf));
815 }
816 
previous_mix_zero(snd_fd * sf)817 static mus_float_t previous_mix_zero(snd_fd *sf)
818 {
819   if (sf->loc < sf->first) return(previous_sound(sf));
820   sf->loc--;
821   return(read_mix_list_samples(sf));
822 }
823 
824 
next_one_mix(snd_fd * sf)825 static mus_float_t next_one_mix(snd_fd *sf)
826 {
827   if (sf->loc > sf->last) return(next_sound(sf)); /* next_sound here refers to whatever follows the mixed portion */
828   return((sf->data[sf->loc++] * sf->fscaler) + read_sample(((reader_mixes *)(sf->mixes))->sfs[0]));
829 }
830 
previous_one_mix(snd_fd * sf)831 static mus_float_t previous_one_mix(snd_fd *sf)
832 {
833   if (sf->loc < sf->first) return(previous_sound(sf));
834   return((sf->data[sf->loc--] * sf->fscaler) + read_sample(((reader_mixes *)(sf->mixes))->sfs[0]));
835 }
836 
837 
next_one_mix_zero(snd_fd * sf)838 static mus_float_t next_one_mix_zero(snd_fd *sf)
839 {
840   if (sf->loc > sf->last) return(next_sound(sf)); /* next_sound here refers to whatever follows the mixed portion */
841   sf->loc++;
842   return(read_sample(((reader_mixes *)(sf->mixes))->sfs[0]));
843 }
844 
previous_one_mix_zero(snd_fd * sf)845 static mus_float_t previous_one_mix_zero(snd_fd *sf)
846 {
847   if (sf->loc < sf->first) return(previous_sound(sf));
848   sf->loc--;
849   return(read_sample(((reader_mixes *)(sf->mixes))->sfs[0]));
850 }
851 
852 
next_mix_ramp1(snd_fd * sf)853 static mus_float_t next_mix_ramp1(snd_fd *sf)
854 {
855   if (sf->loc > sf->last)
856     return(next_sound(sf));
857   return((sf->data[sf->loc++] * next_ramp1_value(sf)) + read_mix_list_samples(sf));
858 }
859 
previous_mix_ramp1(snd_fd * sf)860 static mus_float_t previous_mix_ramp1(snd_fd *sf)
861 {
862   if (sf->loc < sf->first)
863     return(previous_sound(sf));
864   return((sf->data[sf->loc--] * previous_ramp1_value(sf)) + read_mix_list_samples(sf));
865 }
866 
867 
next_mix_ramp2(snd_fd * sf)868 static mus_float_t next_mix_ramp2(snd_fd *sf)
869 {
870   if (sf->loc > sf->last)
871     return(next_sound(sf));
872   return((sf->data[sf->loc++] * next_ramp2_value(sf)) + read_mix_list_samples(sf));
873 }
874 
previous_mix_ramp2(snd_fd * sf)875 static mus_float_t previous_mix_ramp2(snd_fd *sf)
876 {
877   if (sf->loc < sf->first)
878     return(previous_sound(sf));
879   return((sf->data[sf->loc--] * previous_ramp2_value(sf)) + read_mix_list_samples(sf));
880 }
881 
882 
next_mix_ramp(snd_fd * sf)883 static mus_float_t next_mix_ramp(snd_fd *sf)
884 {
885   if (sf->loc > sf->last)
886     return(next_sound(sf));
887   return((sf->data[sf->loc++] * next_ramp_value(sf)) + read_mix_list_samples(sf));
888 }
889 
previous_mix_ramp(snd_fd * sf)890 static mus_float_t previous_mix_ramp(snd_fd *sf)
891 {
892   if (sf->loc < sf->first)
893     return(previous_sound(sf));
894   return((sf->data[sf->loc--] * previous_ramp_value(sf)) + read_mix_list_samples(sf));
895 }
896 
897 
next_mix_xramp1(snd_fd * sf)898 static mus_float_t next_mix_xramp1(snd_fd *sf)
899 {
900   if (sf->loc > sf->last)
901     return(next_sound(sf));
902   return((sf->data[sf->loc++] * READER_SCALER(sf) * next_xramp1_value(sf)) + read_mix_list_samples(sf));
903 }
904 
previous_mix_xramp1(snd_fd * sf)905 static mus_float_t previous_mix_xramp1(snd_fd *sf)
906 {
907   if (sf->loc < sf->first)
908     return(previous_sound(sf));
909   return((sf->data[sf->loc--] * READER_SCALER(sf) * previous_xramp1_value(sf)) + read_mix_list_samples(sf));
910 }
911 
912 
next_mix_xramp(snd_fd * sf)913 static mus_float_t next_mix_xramp(snd_fd *sf)
914 {
915   if (sf->loc > sf->last)
916     return(next_sound(sf));
917   return((sf->data[sf->loc++] * READER_SCALER(sf) * next_xramp_value(sf)) + read_mix_list_samples(sf));
918 }
919 
previous_mix_xramp(snd_fd * sf)920 static mus_float_t previous_mix_xramp(snd_fd *sf)
921 {
922   if (sf->loc < sf->first)
923     return(previous_sound(sf));
924   return((sf->data[sf->loc--] * READER_SCALER(sf) * previous_xramp_value(sf)) + read_mix_list_samples(sf));
925 }
926 
927 
next_mix_xramp_f_ramp_f(snd_fd * sf)928 static mus_float_t next_mix_xramp_f_ramp_f(snd_fd *sf)
929 {
930   if (sf->loc > sf->last)
931     return(next_sound(sf));
932   return((sf->data[sf->loc++] * (*(READER_RAMPF(sf)))(sf)) + read_mix_list_samples(sf));
933 }
934 
previous_mix_xramp_f_ramp_f(snd_fd * sf)935 static mus_float_t previous_mix_xramp_f_ramp_f(snd_fd *sf)
936 {
937   if (sf->loc < sf->first)
938     return(previous_sound(sf));
939   return((sf->data[sf->loc--] * (*(READER_REV_RAMPF(sf)))(sf)) + read_mix_list_samples(sf));
940 }
941 
942 
943 
944 
945 
946 /* this is a kind of state machine for the virtual editor: if, for example, the current
947      op is ed_ramp and we want to add an xramp, we look at the add_xramp field of
948      ed_ramp's type_info struct, and if it's not -1, the add_xramp field is the
949      new virtual editor op; otherwise the op is collapsed to a change edit.
950      So, to add a new entry, make the accessor, and fill in the fields that
951      can reach it.  Since, for example, ramp->xramp is flipped to xramp->ramp
952      since a(b()) == b(a()) in this case, there can be several ways to reach
953      a given op. Then fix the ramp segments in choose_accessor.  The first
954      struct field is redundant, but makes editing this table much easier.
955 */
956 
957 enum {ED_SIMPLE,                ED_MIX,
958       ED_ZERO,                  ED_MIX_ZERO,
959 
960       /* simple envelope special cases */
961       ED_RAMP1,                 ED_MIX_RAMP1,
962       ED_RAMP2,                 ED_MIX_RAMP2,
963       ED_XRAMP1,                ED_MIX_XRAMP1,
964 
965       /* envelopes */
966       ED_RAMP,                  ED_MIX_RAMP,
967       ED_XRAMP,                 ED_MIX_XRAMP,
968       ED_XRAMP_RAMP,            ED_MIX_XRAMP_RAMP,
969 
970       NUM_OPS
971 };
972 
973 
974 typedef struct {
975   int type, add_ramp, add_xramp, add_mix, subtract_mix;
976   bool ramps, zero, mixes; /* zero = no underlying data (mix, etc), mixes = involves virtual mixes in some way */
977   int scale_op;
978   const char *name;
979   mus_float_t (*next)(struct snd_fd *sf);
980   mus_float_t (*previous)(struct snd_fd *sf);
981   mus_float_t (*rampf)(struct snd_fd *sf);
982   mus_float_t (*rev_rampf)(struct snd_fd *sf);
983 } fragment_type_info;
984 
985 
986 enum {NO_SCALE, SIMPLE_SCALE, SCALE_R, SCALE_X};
987 
988 #define NO_RAMP false
989 #define NO_MIX false
990 #define ON_ZERO true
991 #define ON_DATA false
992 #define MIXED true
993 #define RAMPED true
994 
995 static fragment_type_info type_info[NUM_OPS] = {
996 
997   {ED_SIMPLE, ED_RAMP1, ED_XRAMP1, ED_MIX, -1,
998    NO_RAMP, ON_DATA, NO_MIX, SIMPLE_SCALE,
999    "ed_simple", next_sample_value, previous_sample_value,
1000    NULL, NULL},
1001 
1002   {ED_MIX, -1, -1, ED_MIX, ED_SIMPLE,
1003    NO_RAMP, ON_DATA, MIXED, NO_SCALE,
1004    "ed_mix_simple", next_mix, previous_mix,
1005    NULL, NULL},
1006 
1007   {ED_ZERO, ED_ZERO, ED_ZERO, ED_MIX_ZERO, -1,
1008    NO_RAMP, ON_ZERO, NO_MIX, NO_SCALE,
1009    "ed_zero", next_zero, previous_zero,
1010    NULL, NULL},
1011 
1012   {ED_MIX_ZERO, -1, -1, ED_MIX_ZERO, ED_ZERO,
1013    NO_RAMP, ON_ZERO, MIXED, NO_SCALE,
1014    "ed_mix_zero", next_mix_zero, previous_mix_zero,
1015    NULL, NULL},
1016 
1017   {ED_RAMP1, ED_RAMP2, ED_XRAMP_RAMP, ED_MIX_RAMP1, -1,
1018    RAMPED, ON_DATA, NO_MIX, SCALE_R,
1019    "ed_ramp1", next_ramp1, previous_ramp1,
1020    NULL, NULL},
1021 
1022   {ED_MIX_RAMP1, -1, -1, ED_MIX_RAMP1, ED_RAMP1,
1023    RAMPED, ON_DATA, MIXED, SCALE_R,
1024    "ed_mix_ramp1", next_mix_ramp1, previous_mix_ramp1,
1025    NULL, NULL},
1026 
1027   {ED_RAMP2, ED_RAMP, ED_XRAMP_RAMP, ED_MIX_RAMP2, -1,
1028    RAMPED, ON_DATA, NO_MIX, SCALE_R,
1029    "ed_ramp2", next_ramp_f, previous_ramp_f,
1030    next_ramp2_value, previous_ramp2_value},
1031 
1032   {ED_MIX_RAMP2, -1, -1, ED_MIX_RAMP2, ED_RAMP2,
1033    RAMPED, ON_DATA, MIXED, SCALE_R,
1034    "ed_mix_ramp2", next_mix_ramp2, previous_mix_ramp2,
1035    NULL, NULL},
1036 
1037   {ED_XRAMP1, ED_XRAMP_RAMP, ED_XRAMP, ED_MIX_XRAMP1, -1,
1038    RAMPED, ON_DATA, NO_MIX, NO_SCALE,
1039    "ed_xramp1", next_xramp1, previous_xramp1,
1040    NULL, NULL},
1041 
1042   {ED_MIX_XRAMP1, -1, -1, ED_MIX_XRAMP1, ED_XRAMP1,
1043    RAMPED, ON_DATA, MIXED, NO_SCALE,
1044    "ed_mix_xramp1", next_mix_xramp1, previous_mix_xramp1,
1045    NULL, NULL},
1046 
1047   {ED_RAMP, ED_RAMP, ED_XRAMP_RAMP, ED_MIX_RAMP, -1,
1048    RAMPED, ON_DATA, NO_MIX, SCALE_R,
1049    "ed_ramp", next_ramp, previous_ramp,
1050    NULL, NULL},
1051 
1052   {ED_MIX_RAMP, -1, -1, ED_MIX_RAMP, ED_RAMP,
1053    RAMPED, ON_DATA, MIXED, SCALE_R,
1054    "ed_mix_ramp", next_mix_ramp, previous_mix_ramp,
1055    NULL, NULL},
1056 
1057   {ED_XRAMP, ED_XRAMP_RAMP, ED_XRAMP, ED_MIX_XRAMP, -1,
1058    RAMPED, ON_DATA, NO_MIX, NO_SCALE,
1059    "ed_xramp", next_xramp, previous_xramp,
1060    NULL, NULL},
1061 
1062   {ED_MIX_XRAMP, -1, -1, ED_MIX_XRAMP, ED_XRAMP,
1063    RAMPED, ON_DATA, MIXED, NO_SCALE,
1064    "ed_mix_xramp", next_mix_xramp, previous_mix_xramp,
1065    NULL, NULL},
1066 
1067   {ED_XRAMP_RAMP, ED_XRAMP_RAMP, ED_XRAMP_RAMP, ED_MIX_XRAMP_RAMP, -1,
1068    RAMPED, ON_DATA, NO_MIX, SCALE_R,
1069    "ed_xramp_ramp", next_ramp_f, previous_ramp_f,
1070    next_xramp_ramp_value, previous_xramp_ramp_value},
1071 
1072   {ED_MIX_XRAMP_RAMP, -1, -1, ED_MIX_XRAMP_RAMP, ED_XRAMP_RAMP,
1073    RAMPED, ON_DATA, MIXED, SCALE_R,
1074    "ed_mix_xramp_ramp", next_mix_xramp_f_ramp_f, previous_mix_xramp_f_ramp_f,
1075    next_xramp_ramp_value, previous_xramp_ramp_value},
1076 };
1077 
1078 
zero_op(int type)1079 static bool zero_op(int type)
1080 {
1081   return(type_info[type].zero);
1082 }
1083 
1084 
ramp_op(int type)1085 static bool ramp_op(int type)
1086 {
1087   return(type_info[type].ramps);
1088 }
1089 
1090 
is_mix_op(int type)1091 static bool is_mix_op(int type)
1092 {
1093   return(type_info[type].mixes);
1094 }
1095 
is_unmixable_op(int type)1096 static bool is_unmixable_op(int type)
1097 {
1098   return(type_info[type].subtract_mix != -1);
1099 }
1100 
is_mixable_op(int type)1101 static bool is_mixable_op(int type)
1102 {
1103   return(type_info[type].add_mix != -1);
1104 }
1105 
1106 
1107 #define DEBUG_EDIT_TABLES 0
1108 
1109 #if DEBUG_EDIT_TABLES
1110 static int hit_entry[NUM_OPS];
1111 
init_hit_entries(void)1112 static void init_hit_entries(void)
1113 {
1114   int i;
1115   for (i = 0; i < NUM_OPS; i++) hit_entry[i] = 0;
1116 }
1117 
1118 
report_unhit_entries(void)1119 static void report_unhit_entries(void)
1120 {
1121   int i;
1122   for (i = 0; i < NUM_OPS; i++)
1123     if (hit_entry[i] == 0)
1124       {
1125 	if (type_info[i].next)
1126 	  fprintf(stderr, "accessible ");
1127 	fprintf(stderr, "%s unhit\n", type_info[i].name);
1128       }
1129 }
1130 
1131 
check_type_info_entry(int op,int expected_ramps,int expected_xramps,bool is_zero)1132 static void check_type_info_entry(int op, int expected_ramps, int expected_xramps, bool is_zero)
1133 {
1134   hit_entry[op]++;
1135   if (op != type_info[op].type)
1136     fprintf(stderr, "%s type: %d %d\n", type_info[op].name, op, type_info[op].type);
1137   if (op != ED_SIMPLE)
1138     {
1139       if (!type_info[op].next)
1140 	fprintf(stderr, "%s no next\n", type_info[op].name);
1141       if (!type_info[op].previous)
1142 	fprintf(stderr, "%s no previous\n", type_info[op].name);
1143     }
1144   if (is_zero != type_info[op].zero)
1145     fprintf(stderr, "%s zero: %d %d\n", type_info[op].name, is_zero, type_info[op].zero);
1146   if ((type_info[op].add_ramp != -1) && (type_info[op].add_ramp != op))
1147     check_type_info_entry(type_info[op].add_ramp, expected_ramps + 1, expected_xramps, is_zero);
1148   if ((type_info[op].add_xramp != -1) && (type_info[op].add_xramp != op))
1149     check_type_info_entry(type_info[op].add_xramp, expected_ramps, expected_xramps + 1, is_zero);
1150   if ((type_info[op].add_mix != -1) &&
1151       (type_info[op].subtract_mix == -1)) /* not a loop! */
1152     check_type_info_entry(type_info[op].add_mix, expected_ramps, expected_xramps, is_zero);
1153 
1154   if ((!(type_info[op].mixes)) && (type_info[op].subtract_mix != -1))
1155     fprintf(stderr, "%s: mix confused (#f)?\n", type_info[op].name);
1156 
1157   if (type_info[op].subtract_mix != -1)
1158     {
1159       if ((type_info[op].add_mix != -1) &&
1160 	  (type_info[op].add_mix != op))
1161 	fprintf(stderr, "%s add_mix: %s\n", type_info[op].name, type_info[type_info[op].add_mix].name);
1162 
1163       if ((type_info[op].add_ramp != -1) &&
1164 	  (op != ED_MIX_ZERO))
1165 	fprintf(stderr, "%s add_ramp: %s\n", type_info[op].name, type_info[type_info[op].add_ramp].name);
1166       if (type_info[op].add_xramp != -1) fprintf(stderr, "%s add_xramp: %s\n", type_info[op].name, type_info[type_info[op].add_xramp].name);
1167 
1168       if (type_info[type_info[op].subtract_mix].add_mix != op)
1169 	fprintf(stderr, "%s subtract: %s but its add: %s\n",
1170 		type_info[op].name, type_info[type_info[op].subtract_mix].name,
1171 		(type_info[type_info[op].subtract_mix].add_mix != -1) ? type_info[type_info[type_info[op].subtract_mix].add_mix].name : "not an op");
1172     }
1173 }
1174 #endif
1175 
1176 
swap_readers(snd_fd * sf)1177 static void swap_readers(snd_fd *sf)
1178 {
1179   mus_float_t (*rrunf)(struct snd_fd *sf);
1180   rrunf = sf->runf;
1181   sf->runf = sf->rev_runf;
1182   sf->rev_runf = rrunf;
1183 }
1184 
1185 
read_sample_change_direction(snd_fd * sf,read_direction_t dir1)1186 void read_sample_change_direction(snd_fd *sf, read_direction_t dir1) /* can't use "dir" on Mac */
1187 {
1188   /* direction reversal can happen in dac(speed arrow), src gen, or user can call next/previous independent of initial dir */
1189   swap_readers(sf);
1190   sf->direction = dir1;
1191   /* can't optimize anything here -- some accessors have state, but how to handle the loc=-1 case? */
1192   if ((dir1 == READ_FORWARD) && (sf->loc < 0))
1193     sf->loc = 0;
1194   else read_sample(sf);
1195 }
1196 
1197 
protected_next_sample(snd_fd * sf)1198 static mus_float_t protected_next_sample(snd_fd *sf)
1199 {
1200   if (sf->direction == READ_BACKWARD)
1201     read_sample_change_direction(sf, READ_FORWARD);
1202   return(read_sample(sf));
1203 }
1204 
1205 
protected_previous_sample(snd_fd * sf)1206 static mus_float_t protected_previous_sample(snd_fd *sf)
1207 {
1208   if (sf->direction == READ_FORWARD)
1209     read_sample_change_direction(sf, READ_BACKWARD);
1210   return(read_sample(sf));
1211 }
1212 
1213 
1214 /* setting the at_end flag here means that a 0.0 is returned from read-sample and then
1215  *   the caller has to check at-end to see if that 0.0 is real.  Not sure how (or whether)
1216  *   this should be fixed.
1217  */
reader_out_of_data(snd_fd * sf)1218 static void reader_out_of_data(snd_fd *sf)
1219 {
1220   sf->at_eof = true;
1221   sf->runf = end_sample_value;
1222   sf->rev_runf = end_sample_value;
1223 }
1224 
1225 
cancel_reader(snd_fd * sf)1226 static snd_fd *cancel_reader(snd_fd *sf)
1227 {
1228   sf->current_sound = NULL;
1229   sf->cbi = 0;
1230   reader_out_of_data(sf);
1231   return(sf);
1232 }
1233 
1234 
1235 static reader_mixes *free_reader_mixes(reader_mixes *md);
1236 
setup_mix(snd_fd * sf)1237 static void setup_mix(snd_fd *sf)
1238 {
1239   reader_mixes *md;
1240   int i, list_size, active_mixes = 0;
1241 
1242   if (sf->mixes)
1243     sf->mixes = (void *)free_reader_mixes((reader_mixes *)(sf->mixes));
1244 
1245   md = (reader_mixes *)calloc(1, sizeof(reader_mixes));
1246   sf->mixes = (void *)md;
1247 
1248   list_size = READER_MIX_LIST_SIZE(sf);
1249   for (i = 0; i < list_size; i++)
1250     if ((READER_MIX_STATE(sf, i)) &&
1251 	(READER_MIX_SCALER(sf, i) != 0.0))
1252       active_mixes++;
1253 
1254   md->size = active_mixes;
1255   if (md->size > 0)
1256     {
1257       int j = 0;
1258       md->sfs = (snd_fd **)calloc(md->size, sizeof(snd_fd *));
1259       for (i = 0; i < list_size; i++)
1260 	if ((READER_MIX_STATE(sf, i)) &&
1261 	    (READER_MIX_SCALER(sf, i) != 0.0))
1262 	  md->sfs[j++] = make_virtual_mix_reader(sf->cp,
1263 						 sf->frag_pos + READER_GLOBAL_POSITION(sf) - READER_MIX_BEG(sf, i), /* global position - mix position + fragment start loc */
1264 						 READER_MIX_LENGTH(sf, i),
1265 						 READER_MIX_INDEX(sf, i),
1266 						 READER_MIX_SCALER(sf, i),
1267 						 sf->direction);
1268       if (active_mixes == 1)
1269 	{
1270 	  if (READER_TYPE(sf) == ED_MIX)
1271 	    {
1272 	      sf->runf = next_one_mix;
1273 	      sf->rev_runf = previous_one_mix;
1274   	    }
1275 	  else
1276 	    {
1277 	      if (READER_TYPE(sf) == ED_MIX_ZERO)
1278 		{
1279 		  sf->runf = next_one_mix_zero;
1280 		  sf->rev_runf = previous_one_mix_zero;
1281 		}
1282 	    }
1283 	}
1284     }
1285 }
1286 
1287 
setup_ramps(snd_fd * sf,int typ)1288 static void setup_ramps(snd_fd *sf, int typ)
1289 {
1290   int rmps, xrmps;
1291   ed_fragment *ed;
1292   ed = (ed_fragment *)(sf->cb);
1293 
1294   rmps = ED_RAMP_LIST_SIZE(ed);
1295   xrmps = ED_XRAMP_LIST_SIZE(ed);
1296 
1297   if (!sf->ramps)
1298     sf->ramps = (void *)calloc(1, sizeof(reader_ramps));
1299 
1300   /* make sure the ramp arrays are large enough (we'll free them at the end of the read) */
1301 
1302   if (rmps != READER_RAMPS(sf))
1303     {
1304       if (READER_RAMPS(sf) > 0)
1305 	{
1306 	  free(READER_INCRS(sf));
1307 	  READER_INCRS(sf) = NULL;
1308 	  free(READER_VALS(sf));
1309 	  READER_VALS(sf) = NULL;
1310 	}
1311       if (rmps > 0)
1312 	{
1313 	  READER_INCRS(sf) = (double *)calloc(rmps, sizeof(double));
1314 	  READER_VALS(sf) = (double *)calloc(rmps, sizeof(double));
1315 	}
1316       READER_RAMPS(sf) = rmps;                    /* this has to match the actual ramp number */
1317     }
1318 
1319   if (xrmps != READER_XRAMPS(sf))
1320     {
1321       if (READER_XRAMPS(sf) > 0)
1322 	{
1323 	  free(READER_XINCRS(sf));
1324 	  READER_XINCRS(sf) = NULL;
1325 	  free(READER_XVALS(sf));
1326 	  READER_XVALS(sf) = NULL;
1327 	}
1328       if (xrmps > 0)
1329 	{
1330 	  READER_XINCRS(sf) = (double *)calloc(xrmps, sizeof(double));
1331 	  READER_XVALS(sf) = (double *)calloc(xrmps, sizeof(double));
1332 	}
1333       READER_XRAMPS(sf) = xrmps;
1334     }
1335 
1336   if (rmps > 0)
1337     {
1338       int i;
1339       for (i = 0; i < rmps; i++)
1340 	{
1341 	  READER_INCR(sf, i) = READER_RAMP_INCR(sf, i);
1342 	  READER_VAL(sf, i) = READER_RAMP_START(sf, i) + READER_INCR(sf, i) * sf->frag_pos;
1343 	}
1344     }
1345 
1346   if (xrmps > 0)
1347     {
1348       int i;
1349       for (i = 0; i < xrmps; i++)
1350 	{
1351 	  READER_XINCR(sf, i) = READER_XRAMP_INCR(sf, i);
1352 	  READER_XVAL(sf, i) = READER_XRAMP_START(sf, i) * exp(log(READER_XRAMP_INCR(sf, i)) * sf->frag_pos);
1353 	}
1354     }
1355 
1356   READER_RAMPF(sf) = type_info[typ].rampf;
1357   READER_REV_RAMPF(sf) = type_info[typ].rev_rampf;
1358 }
1359 
1360 
scale_ramp(snd_fd * sf,int rmp,mus_float_t scl)1361 static void scale_ramp(snd_fd *sf, int rmp, mus_float_t scl)
1362 {
1363   READER_INCR(sf, rmp) *= scl;
1364   READER_VAL(sf, rmp) *= scl;
1365 }
1366 
1367 
scale_inputs(snd_fd * sf,int scl_type)1368 static void scale_inputs(snd_fd *sf, int scl_type)
1369 {
1370   switch (scl_type)
1371     {
1372     case NO_SCALE:
1373       break;
1374 
1375     case SIMPLE_SCALE:
1376       /* ED_SIMPLE special case */
1377       if ((READER_SCALER(sf) == 1.0) &&
1378 	  (sf->fscaler == 1.0))
1379 	{
1380 	  sf->runf = next_sample_value_unscaled;
1381 	  sf->rev_runf = previous_sample_value_unscaled;
1382 	}
1383       break;
1384 
1385     case SCALE_R:
1386       scale_ramp(sf, 0, READER_SCALER(sf));
1387       break;
1388     }
1389 }
1390 
1391 
choose_accessor(snd_fd * sf)1392 static void choose_accessor(snd_fd *sf)
1393 {
1394   int typ;
1395   /* fragment-specific reader choice */
1396 
1397   typ = READER_TYPE(sf);
1398 
1399   if (ramp_op(typ))
1400     setup_ramps(sf, typ);
1401 
1402   if (is_mix_op(typ))
1403     setup_mix(sf);
1404 
1405   sf->runf = type_info[typ].next;
1406   sf->rev_runf = type_info[typ].previous;
1407 
1408   scale_inputs(sf, type_info[typ].scale_op);
1409 
1410   if (sf->direction == READ_BACKWARD) swap_readers(sf);
1411 }
1412 
1413 
1414 static const char *edit_names[NUM_EDIT_TYPES] = {"insert", "delete", "set", "init", "scale", "zero", "env", "extend", "mix", "change mix"};
1415 
1416 
display_ed_list(chan_info * cp,FILE * outp,int i,ed_list * ed)1417 static void display_ed_list(chan_info *cp, FILE *outp, int i, ed_list *ed)
1418 {
1419   int len, j;
1420   snd_data *sd;
1421   if (!ed)
1422     {
1423       fprintf(outp, "\n (NULL FRAGMENT at %d)", i);
1424       return;
1425     }
1426   if (i >= cp->edit_size)
1427     {
1428       fprintf(outp, "\n (BOGUS FRAGMENT at %d of %d)", i, cp->edit_size);
1429       return;
1430     }
1431   len = ed->size; /* number of fragments in this list */
1432   switch (ed->edit_type)
1433     {
1434     case INSERTION_EDIT:  fprintf(outp, "\n (insert %" print_mus_long " %" print_mus_long ") ", ed->beg, ed->len);                        break;
1435     case DELETION_EDIT:   fprintf(outp, "\n (delete %" print_mus_long " %" print_mus_long ") ", ed->beg, ed->len);                        break;
1436     case CHANGE_EDIT:     fprintf(outp, "\n (set %" print_mus_long " %" print_mus_long ") ", ed->beg, ed->len);                           break;
1437     case SCALED_EDIT:     fprintf(outp, "\n (scale %" print_mus_long " %" print_mus_long ") ", ed->beg, ed->len);                         break;
1438     case ZERO_EDIT:       fprintf(outp, "\n (silence %" print_mus_long " %" print_mus_long ") ", ed->beg, ed->len);                       break;
1439     case RAMP_EDIT:       fprintf(outp, "\n (ramp %" print_mus_long " %" print_mus_long ") ", ed->beg, ed->len);                          break;
1440     case EXTEND_EDIT:     fprintf(outp, "\n (extend edit list with no-op)");                                            break;
1441     case MIX_EDIT:        fprintf(outp, "\n (mix %" print_mus_long " %" print_mus_long ") ", ed->beg, ed->len);                           break;
1442     case CHANGE_MIX_EDIT: fprintf(outp, "\n (change mix %" print_mus_long " %" print_mus_long ") ", ed->beg, ed->len);                    break;
1443     case INITIALIZE_EDIT: fprintf(outp, "\n (begin) ");                                                                 break;
1444     default: break;
1445     }
1446   if (ed->origin) fprintf(outp, "; %s ", ed->origin);
1447   fprintf(outp, "[%d:%d]:", i, len);
1448   for (j = 0; j < len; j++)
1449     {
1450       int index;
1451       index = FRAGMENT_SOUND(ed, j);
1452       if (index == EDIT_LIST_END_MARK)
1453 	fprintf(outp, "\n   (at %" print_mus_long ", end_mark)", FRAGMENT_GLOBAL_POSITION(ed, j));
1454       else
1455 	{
1456 	  int typ;
1457 	  typ = FRAGMENT_TYPE(ed, j);
1458 	  fprintf(outp, "\n   (at %" print_mus_long ", cp->sounds[%d][%" print_mus_long ":%" print_mus_long ", %.3f",
1459 		  FRAGMENT_GLOBAL_POSITION(ed, j),
1460 		  index,
1461 		  FRAGMENT_LOCAL_POSITION(ed, j),
1462 		  FRAGMENT_LOCAL_END(ed, j),
1463 		  FRAGMENT_SCALER(ed, j));
1464 
1465 	  if (ramp_op(typ))
1466 	    {
1467 	      int k;
1468 	      for (k = 0; k < FRAGMENT_RAMP_LIST_SIZE(ed, j); k++)  /* step envs become successive scalings */
1469 		fprintf(outp, ", [%d]%.3f -> %.3f", k + 1,
1470 			FRAGMENT_RAMP_START(ed, j, k),
1471 			FRAGMENT_RAMP_START(ed, j, k) + (FRAGMENT_LENGTH(ed, j) - 1) * FRAGMENT_RAMP_INCR(ed, j, k));
1472 
1473 	      for (k = 0; k < FRAGMENT_XRAMP_LIST_SIZE(ed, j); k++)
1474 		{
1475 		  double y;
1476 		  y = FRAGMENT_XRAMP_SCALER(ed, j, k) * FRAGMENT_XRAMP_START(ed, j, k);
1477 		  fprintf(outp, ", [%d]%.3f -> %.3f, off: %.3f, scl: %.3f",
1478 			  k + 1 + FRAGMENT_RAMP_LIST_SIZE(ed, j),
1479 			  FRAGMENT_XRAMP_OFFSET(ed, j, k) + y,
1480 			  FRAGMENT_XRAMP_OFFSET(ed, j, k) + y * exp(log(FRAGMENT_XRAMP_INCR(ed, j, k)) * (FRAGMENT_LENGTH(ed, j) - 1)),
1481 			  FRAGMENT_XRAMP_OFFSET(ed, j, k),
1482 			  FRAGMENT_XRAMP_SCALER(ed, j, k));
1483 		}
1484 	    }
1485 
1486 	  if (is_mix_op(typ))
1487 	    {
1488 	      ed_mixes *mxs;
1489 	      mxs = FRAGMENT_MIXES(ed, j);
1490 	      if (!mxs)
1491 		fprintf(outp, ", %s but no mixes found?", type_info[typ].name);
1492 	      else
1493 		{
1494 		  int i;
1495 		  for (i = 0; i < mxs->size; i++)
1496 		    {
1497 		      if (MIX_LIST_STATE(mxs, i))
1498 			fprintf(outp, ", ([%d]: %d %.3f %" print_mus_long ")",
1499 				i,
1500 				MIX_LIST_INDEX(mxs, i),
1501 				MIX_LIST_SCALER(mxs, i),
1502 				FRAGMENT_GLOBAL_POSITION(ed, j) - MIX_LIST_BEG(mxs, i));
1503 		    }
1504 		}
1505 	    }
1506 	  fprintf(outp, "])");
1507 	  if (index != EDIT_LIST_ZERO_MARK)
1508 	    {
1509 	      sd = cp->sounds[index];
1510 	      if (!sd)
1511 		fprintf(outp, " [nil!]");
1512 	      else
1513 		if (sd->type == SND_DATA_FILE)
1514 		  fprintf(outp, " [file: %s[%d]]", sd->filename, sd->chan);
1515 		else
1516 		  if (sd->type == SND_DATA_BUFFER)
1517 		    fprintf(outp, " [buf: %" print_mus_long "] ", sd->data_bytes / sizeof(mus_float_t));
1518 		  else fprintf(outp, " [bogus!]");
1519 	    }
1520 	}
1521     }
1522   fprintf(outp, "\n");
1523 }
1524 
1525 
edit_changes_begin_at(chan_info * cp,int edpos)1526 mus_long_t edit_changes_begin_at(chan_info *cp, int edpos)
1527 {
1528   return(cp->edits[edpos]->beg);
1529 }
1530 
1531 
edit_changes_end_at(chan_info * cp,int edpos)1532 mus_long_t edit_changes_end_at(chan_info *cp, int edpos)
1533 {
1534   /* the env code assumes a deletion passes in the number deleted so that the copy knows where to start in the old env */
1535   return(cp->edits[edpos]->beg + cp->edits[edpos]->len);
1536 }
1537 
1538 
1539 /* ---------------- edit list display, save, etc ---------------- */
1540 
edit_to_string(chan_info * cp,int edit)1541 char *edit_to_string(chan_info *cp, int edit)
1542 {
1543   ed_list *ed;
1544   ed = cp->edits[edit];
1545   /* only for edit list in snd-g|xchn.c */
1546 
1547 #if HAVE_FORTH
1548   return(mus_format("%s : %" print_mus_long " %" print_mus_long " %s",
1549 		    ed->origin,
1550 		    ed->beg, ed->len,
1551 		    edit_names[(int)(ed->edit_type)]));
1552 #endif
1553 
1554 #if HAVE_RUBY
1555   return(mus_format("%s : %s(%" print_mus_long ", %" print_mus_long ")",
1556 		    ed->origin,
1557 		    edit_names[(int)(ed->edit_type)],
1558 		    ed->beg, ed->len));
1559 
1560 #endif
1561 
1562 #if HAVE_SCHEME
1563   return(mus_format("%s : (%s %" print_mus_long " %" print_mus_long ")",
1564 		    ed->origin,
1565 		    edit_names[(int)(ed->edit_type)],
1566 		    ed->beg, ed->len));
1567 #endif
1568 
1569   return(NULL);
1570 }
1571 
1572 
display_edits(chan_info * cp,FILE * outp)1573 static void display_edits(chan_info *cp, FILE *outp)
1574 {
1575   int i;
1576   fprintf(outp, "\nEDITS: %d\n", cp->edit_ctr);
1577   for (i = 0; i <= cp->edit_ctr; i++)
1578     display_ed_list(cp, outp, i, cp->edits[i]);
1579 }
1580 
1581 
snd_make_file(const char * ofile,int chans,file_info * hdr,snd_fd ** sfs,mus_long_t length,bool report_ok)1582 static io_error_t snd_make_file(const char *ofile, int chans, file_info *hdr, snd_fd **sfs, mus_long_t length, bool report_ok)
1583 {
1584   /* create ofile, fill it by following sfs, use hdr for srate/type/format decisions */
1585   int ofd;
1586   int i, j, datumb;
1587   bool reporting = false;
1588   mus_long_t len = 0, total = 0, alloc_len;
1589   chan_info *cp = NULL;
1590   mus_float_t **obufs;
1591   io_error_t io_err = IO_NO_ERROR;
1592   int sl_err = MUS_NO_ERROR;
1593   bool need_clipping;
1594 
1595   ofd = open_temp_file(ofile, chans, hdr, &io_err);
1596   if (ofd == -1)
1597     return(io_err);
1598   alloc_len = length;
1599   if (alloc_len > REPORTING_SIZE)
1600     alloc_len = REPORTING_SIZE;
1601 
1602   need_clipping = clipping(ss);
1603   if (need_clipping)
1604     {
1605       bool max_ok = false;
1606       mus_float_t mx = 0.0;
1607       for (i = 0; i < chans; i++)
1608 	{
1609 	  mus_float_t cur_mx;
1610 	  chan_info *ncp;
1611 	  int edpos;
1612 
1613 	  ncp = sfs[i]->cp;
1614 	  edpos = sfs[i]->edit_ctr;
1615 
1616 	  if (peak_env_maxamp_ok(ncp, edpos))
1617 	    cur_mx = peak_env_maxamp(ncp, edpos);
1618 	  else
1619 	    {
1620 	      cur_mx = ed_maxamp(ncp, edpos);
1621 	      if (cur_mx < 0.0) break;
1622 	    }
1623 
1624 	  if (cur_mx > mx)
1625 	    mx = cur_mx;
1626 	  if (i == (chans - 1))
1627 	    max_ok = true;
1628 	}
1629 
1630       if ((max_ok) &&
1631 	  (mx <= 1.0))
1632 	need_clipping = false;
1633     }
1634 
1635   mus_file_set_clipping(ofd, need_clipping); /* clipping is very expensive, so try to avoid it */
1636 
1637   datumb = mus_bytes_per_sample(hdr->sample_type);
1638   ss->stopped_explicitly = false;
1639 
1640   obufs = (mus_float_t **)malloc(chans * sizeof(mus_float_t *));
1641   for (i = 0; i < chans; i++)
1642     obufs[i] = (mus_float_t *)calloc(alloc_len, sizeof(mus_float_t));
1643   j = 0;
1644 
1645   reporting = ((report_ok) && (length > alloc_len));
1646   if (reporting)
1647     {
1648       cp = sfs[0]->cp;
1649       start_progress_report(cp);
1650     }
1651 
1652   if (chans == 1)
1653     {
1654       mus_float_t *buf;
1655       snd_fd *sf;
1656       buf = obufs[0];
1657       sf = sfs[0];
1658       if (length > alloc_len)
1659 	{
1660 	  sampler_set_safe(sf, length);
1661 	  for (len = 0; len < length; )
1662 	    {
1663 	      int k, kdur;
1664 
1665 	      kdur = length - len;
1666 	      if (kdur > alloc_len) kdur = alloc_len;
1667 
1668 	      for (k = 0; k < kdur; k++)
1669 		buf[k] = read_sample(sf);
1670 
1671 	      sl_err = mus_file_write(ofd, 0, kdur - 1, 1, obufs);
1672 	      j = 0;
1673 	      if (sl_err != MUS_NO_ERROR) break;
1674 	      if (reporting)
1675 		{
1676 		  total += kdur;
1677 		  progress_report(cp, (mus_float_t)((double)total / (double)length));
1678 		}
1679 	      len += kdur;
1680 	    }
1681 	}
1682       else
1683 	{
1684 	  samples_to_vct_with_reader(length, buf, sf);
1685 	  len = length;
1686 	  j = (int)length;
1687 	}
1688     }
1689   else
1690     {
1691       if (length > alloc_len)
1692 	{
1693 	  mus_float_t *buf;
1694 	  snd_fd *sf;
1695 
1696 	  for (i = 0; i < chans; i++)
1697 	    sampler_set_safe(sfs[i], length);
1698 
1699 	  for (len = 0; len < length;)
1700 	    {
1701 	      int k, kdur;
1702 	      kdur = length - len;
1703 	      if (kdur > alloc_len) kdur = alloc_len;
1704 
1705 	      for (i = 0; i < chans; i++)
1706 		{
1707 		  buf = obufs[i];
1708 		  sf = sfs[i];
1709 		  for (k = 0; k < kdur; k++)
1710 		    buf[k] = read_sample(sf);
1711 		}
1712 
1713 	      sl_err = mus_file_write(ofd, 0, kdur - 1, chans, obufs);
1714 	      j = 0;
1715 	      if (sl_err != MUS_NO_ERROR) break;
1716 	      if (reporting)
1717 		{
1718 		  total += kdur;
1719 		  progress_report(cp, (mus_float_t)((double)total / (double)length));
1720 		}
1721 	      len += kdur;
1722 	    }
1723 	}
1724       else
1725 	{
1726 	  for (i = 0; i < chans; i++)
1727 	    samples_to_vct_with_reader(length, obufs[i], sfs[i]);
1728 	  len = length;
1729 	  j = (int)length;
1730 	}
1731     }
1732   if ((sl_err == MUS_NO_ERROR) &&
1733       (j > 0))
1734     sl_err = mus_file_write(ofd, 0, j - 1, chans, obufs);
1735   if (sl_err == MUS_NO_ERROR)
1736     {
1737       io_err = close_temp_file(ofile, ofd, hdr->type, len * chans * datumb);
1738 #if USE_MOTIF
1739       if (!(ss->file_monitor_ok))
1740 	alert_new_file();
1741 #endif
1742     }
1743   else
1744     {
1745       mus_file_close(ofd);
1746       io_err = sndlib_error_to_snd(sl_err);
1747     }
1748   if (reporting) finish_progress_report(cp);
1749   for (i = 0; i < chans; i++) free(obufs[i]);
1750   free(obufs);
1751   return(io_err);
1752 }
1753 
1754 
channel_to_file_with_bounds(chan_info * cp,const char * ofile,int edpos,mus_long_t beg,mus_long_t len,file_info * hdr,bool report_ok)1755 static io_error_t channel_to_file_with_bounds(chan_info *cp, const char *ofile, int edpos, mus_long_t beg, mus_long_t len, file_info *hdr, bool report_ok)
1756 {
1757   snd_info *sp;
1758   snd_fd **sf;
1759   io_error_t err = IO_NO_ERROR;
1760   sp = cp->sound;
1761   sf = (snd_fd **)malloc(sizeof(snd_fd *));
1762 
1763   sf[0] = init_sample_read_any_with_bufsize(beg, cp, READ_FORWARD, edpos, len);
1764   if (!sf[0])
1765     {
1766       free(sf);
1767       snd_error("no such edit: %s[%d]: %d (this channel has %d edit%s",
1768 		sp->short_filename,
1769 		cp->chan,
1770 		edpos,
1771 		cp->edit_ctr,
1772 		(cp->edit_ctr == 1) ? "" : "s");
1773     }
1774   else
1775     {
1776       err = snd_make_file(ofile, 1, hdr, sf, len, report_ok);
1777       free_snd_fd(sf[0]);
1778       free(sf);
1779       if ((err != IO_NO_ERROR) &&
1780 	  (err != IO_INTERRUPTED))
1781 	snd_error("can't save %s chan %d: %s %s",
1782 		  sp->short_filename,
1783 		  cp->chan,
1784 		  ofile,
1785 		  snd_io_strerror());
1786     }
1787   return(err);
1788 }
1789 
1790 
channel_to_file(chan_info * cp,const char * ofile,int edpos)1791 static io_error_t channel_to_file(chan_info *cp, const char *ofile, int edpos) /* preserves cp->sound's header settings */
1792 {
1793   return(channel_to_file_with_bounds(cp, ofile, edpos, 0, cp->edits[edpos]->samples, cp->sound->hdr, true));
1794 }
1795 
1796 
channel_to_file_with_settings(chan_info * cp,const char * new_name,int srate,mus_sample_t samp_type,mus_header_t hd_type,const char * comment,int pos)1797 io_error_t channel_to_file_with_settings(chan_info *cp, const char *new_name, int srate,
1798 					 mus_sample_t samp_type, mus_header_t hd_type, const char *comment, int pos)
1799 {
1800   file_info *hdr, *ohdr;
1801   snd_info *sp;
1802   io_error_t err = IO_NO_ERROR;
1803   sp = cp->sound;
1804   ohdr = sp->hdr;
1805   hdr = copy_header(new_name, ohdr);
1806   hdr->sample_type = samp_type;
1807   hdr->srate = srate;
1808   hdr->type = hd_type;
1809   if (comment)
1810     hdr->comment = mus_strdup(comment);
1811   else hdr->comment = NULL;
1812   hdr->data_location = 0; /* in case comment changes it */
1813 
1814   if (pos == AT_CURRENT_EDIT_POSITION)
1815     pos = cp->edit_ctr;
1816 
1817   if ((mus_strcmp(new_name, sp->filename)) &&      /* overwriting current file with one of its channels */
1818       ((sp->user_read_only == FILE_READ_ONLY) ||
1819        (sp->file_read_only == FILE_READ_ONLY)))
1820     {
1821       snd_error("can't save channel %d as %s (%s is write-protected)", cp->chan, new_name, sp->short_filename);
1822       return(IO_WRITE_PROTECTED);
1823     }
1824 
1825   err = channel_to_file_with_bounds(cp, new_name, pos, 0, cp->edits[pos]->samples, hdr, true);
1826 
1827   free_file_info(hdr);
1828   return(err);
1829 }
1830 
1831 
1832 
1833 
1834 /* these are used internally by the save-state process */
1835 #define S_change_samples_with_origin    "change-samples-with-origin"
1836 #define S_insert_samples_with_origin    "insert-samples-with-origin"
1837 #define S_override_samples_with_origin  "override-samples-with-origin"
1838 
fprintf_with_possible_embedded_string(FILE * fd,const char * str)1839 static void fprintf_with_possible_embedded_string(FILE *fd, const char *str)
1840 {
1841   int i, len;
1842   len = mus_strlen(str);
1843   fputc('"', fd);
1844   for (i = 0; i < len; i++)
1845     {
1846       if (str[i] == '"')
1847 	fputc('\\', fd);
1848       fputc(str[i], fd);
1849     }
1850   fputc('"', fd);
1851 }
1852 
1853 
edit_list_data_to_temp_file(chan_info * cp,ed_list * ed,file_delete_t delete_me,bool with_save_state_hook)1854 static char *edit_list_data_to_temp_file(chan_info *cp, ed_list *ed, file_delete_t delete_me, bool with_save_state_hook)
1855 {
1856   snd_data *sd;
1857   char *ofile;
1858   if (with_save_state_hook)
1859     {
1860       char *nfile;
1861       nfile = shorter_tempnam(save_dir(ss), "snd_");
1862       ofile = run_save_state_hook(nfile);
1863       free(nfile);
1864     }
1865   else ofile = shorter_tempnam(save_dir(ss), "snd_");
1866   sd = cp->sounds[ed->sound_location];
1867   if (sd->type == SND_DATA_BUFFER)
1868     mus_array_to_file(ofile, sd->buffered_data, ed->len, DEFAULT_OUTPUT_SRATE, 1);
1869   else
1870     {
1871       io_error_t io_err;
1872       io_err = copy_file(sd->filename, ofile);
1873       if (io_err != IO_NO_ERROR)
1874 	{
1875 	  if (io_err == IO_CANT_OPEN_FILE)
1876 	    snd_warning("%s edit list original temp file %s: %s", io_error_name(io_err), sd->filename, snd_io_strerror());
1877 	  else snd_warning("%s edit list saved temp file %s: %s", io_error_name(io_err), ofile, snd_io_strerror());
1878 	}
1879     }
1880   if (delete_me == DELETE_ME) remember_temp(ofile, 1); /* deletion upon exit (forget_temps) if a temp (edit-list->function, but not save-state) */
1881   return(ofile);
1882 }
1883 
1884 
1885 #if HAVE_FORTH
1886 /*
1887  * ret_name: last word in origin (function name)
1888  *     func: rest-origin (must be freed)
1889  */
split_origin(char * origin,char ** ret_name)1890 static char *split_origin(char *origin, char **ret_name)
1891 {
1892   if (origin && *origin)
1893     {
1894       char *func = (char *)calloc(strlen(origin), sizeof(char));
1895       if ((*ret_name = strrchr(origin, ' ')))
1896 	{
1897 	  (*ret_name)++;
1898 	  memcpy(func, origin, strlen(origin) - strlen(*ret_name) - 1);
1899 	}
1900       else *ret_name = origin;
1901       return(func);
1902     }
1903   return NULL;
1904 }
1905 #endif
1906 
1907 
edit_history_to_file(FILE * fd,chan_info * cp,bool with_save_state_hook)1908 void edit_history_to_file(FILE *fd, chan_info *cp, bool with_save_state_hook)
1909 {
1910   /* write edit list as a scheme|ruby|forth program to fd (open for writing) for subsequent load */
1911   /*   the entire current list is written, then the edit_ctr is fixed up to reflect its current state */
1912   int i, edits;
1913 #if HAVE_FORTH
1914   char *forth_func = NULL;
1915   bool mix_ed = false;
1916 #endif
1917   edits = cp->edit_ctr;
1918   while ((edits < (cp->edit_size - 1)) &&
1919 	 (cp->edits[edits + 1]))
1920     edits++;
1921   /* 0 case = open-sound */
1922   for (i = 1; i <= edits; i++)
1923     {
1924       ed_list *ed;
1925       ed = cp->edits[i];
1926       if (ed)
1927 	{
1928           if (ed->backed_up && (ed->edit_type != MIX_EDIT))
1929 	    {
1930 	      /* as-one-edit (and internally backup_edit_list) remove edit history entries,
1931 	       * making it impossible to reconstruct exactly the edit sequence in save/restore.
1932 	       * The backed_up flag is set in the backed-up entry, and for save/restore, we
1933 	       * override the entire current sound with a saved file.
1934 	       */
1935 	      char *nfile = NULL;
1936 	      mus_long_t len;
1937 	      io_error_t io_err;
1938 	      if (with_save_state_hook)
1939 		{
1940 		  char *ofile;
1941 		  ofile = shorter_tempnam(save_dir(ss), "snd_");
1942 		  nfile = run_save_state_hook(ofile);
1943 		  free(ofile);
1944 		}
1945 	      else nfile = shorter_tempnam(save_dir(ss), "snd_");
1946 	      len = cp->edits[i]->samples;
1947 	      io_err = channel_to_file(cp, nfile, i);
1948 
1949 	      if (io_err != IO_NO_ERROR)
1950 		{
1951 		  /* error is trapped at lower level and pulled up via redirection */
1952 		  free(nfile);
1953 		  return;
1954 		}
1955 #if HAVE_RUBY
1956 	      fprintf(fd, "      %s(\"%s\", %" print_mus_long ", sfile, %d, ", to_proc_name(S_override_samples_with_origin), nfile, len, cp->chan);
1957 	      if (ed->origin)
1958 		fprintf_with_possible_embedded_string(fd, ed->origin);
1959 	      else fprintf(fd, "\"\"");
1960  	      fprintf(fd, ", [%d, %" print_mus_long "])\n",
1961  		      (int)mus_sound_write_date(nfile),
1962  		      mus_sound_length(nfile));
1963 #endif
1964 #if HAVE_SCHEME
1965 	      fprintf(fd, "      (%s \"%s\" %" print_mus_long " sfile %d ", S_override_samples_with_origin, nfile, len, cp->chan);
1966 	      if (ed->origin)
1967 		fprintf_with_possible_embedded_string(fd, ed->origin);
1968 	      else fprintf(fd, "\"\"");
1969 	      fprintf(fd, " (list %d %" print_mus_long "))\n",
1970 		      (int)mus_sound_write_date(nfile),
1971 		      mus_sound_length(nfile));
1972 #endif
1973 #if HAVE_FORTH
1974 	      fprintf(fd, "      \"%s\" %" print_mus_long " sfile %d ", nfile, len, cp->chan);
1975 	      if (ed->origin)
1976 		fprintf_with_possible_embedded_string(fd, ed->origin);
1977 	      else fprintf(fd, "\"\"");
1978  	      fprintf(fd, " '( %d %" print_mus_long " ) %s drop\n",
1979  		      (int)mus_sound_write_date(nfile),
1980  		      mus_sound_length(nfile),
1981 		      S_override_samples_with_origin);
1982 #endif
1983 	      free(nfile);
1984 	    }
1985 	  else
1986 	    {
1987 	      char *nfile = NULL;
1988 #if HAVE_RUBY || HAVE_FORTH
1989 	      fprintf(fd, "      ");
1990 #endif
1991 #if HAVE_SCHEME
1992 	      fprintf(fd, "      (");
1993 #endif
1994 #if HAVE_FORTH
1995 	      switch (ed->edit_type)
1996 		{
1997 		case INSERTION_EDIT:
1998 		  /* samp data snd chn */
1999 		  forth_func = S_insert_samples_with_origin;
2000 		  fprintf(fd, "%" print_mus_long " %" print_mus_long " ",
2001 			  ed->beg,
2002 			  ed->len);
2003 		  if (ed->origin)
2004 		    fprintf_with_possible_embedded_string(fd, ed->origin);
2005 		  else fprintf(fd, "\"%s\"", S_insert_samples);
2006 		  nfile = edit_list_data_to_temp_file(cp, ed, DONT_DELETE_ME, with_save_state_hook);
2007 		  fprintf(fd, " \"%s\" sfile %d", nfile, cp->chan);
2008 		  break;
2009 
2010 		case DELETION_EDIT:
2011 		  /* samp samps snd chn */
2012 		  forth_func = S_delete_samples;
2013 		  fprintf(fd, "%" print_mus_long " %" print_mus_long " sfile %d",
2014 			  ed->beg,
2015 			  ed->len,
2016 			  cp->chan);
2017 		  break;
2018 
2019 		case CHANGE_EDIT:
2020 		  forth_func = S_change_samples_with_origin;
2021 		  fprintf(fd, "%" print_mus_long " %" print_mus_long " ",
2022 			  ed->beg,
2023 			  ed->len);
2024 		  if (ed->origin)
2025 		    fprintf_with_possible_embedded_string(fd, ed->origin);
2026 		  else fprintf(fd, "\"\"");
2027 		  nfile = edit_list_data_to_temp_file(cp, ed, DONT_DELETE_ME, with_save_state_hook);
2028 		  fprintf(fd, " \"%s\" sfile %d", nfile, cp->chan);
2029 		  break;
2030 
2031 		case EXTEND_EDIT:
2032 		  /* not currently saveable (this is a dummy edit fragment for zero-mix-drag position change) */
2033 		  break;
2034 
2035 		case ZERO_EDIT:
2036 		  forth_func = S_pad_channel;
2037 		  fprintf(fd, "%" print_mus_long " %" print_mus_long " sfile %d",
2038 			  ed->beg,
2039 			  ed->len,
2040 			  cp->chan);
2041 		  break;
2042 
2043 		case SCALED_EDIT:
2044 		case RAMP_EDIT:
2045 		  {
2046 		    char *func;
2047 		    if ((func = split_origin(ed->origin, &forth_func)))
2048 		      {
2049 			fprintf(fd, "%s sfile %d", func, cp->chan);
2050 			free(func);
2051 		      }
2052 		    else fprintf(fd, "sfile %d", cp->chan);
2053 		  }
2054 		  break;
2055 
2056 		case MIX_EDIT:
2057 		case CHANGE_MIX_EDIT:
2058 		  mix_ed = true;
2059 		  fprintf(fd, "sfile value snd\n");
2060 		  fprintf(fd, "      %d     value chn\n", cp->chan);
2061 		  fprintf(fd, "      ");
2062 		  forth_func = ed->origin;
2063 		  break;
2064 
2065 		default:
2066 		  snd_error("unknown edit branch: %s: %d %d",
2067 			    ed->origin,
2068 			    ed->edit_type,
2069 			    ed->sound_location);
2070 		  break;
2071 		}
2072 #else
2073 	      switch (ed->edit_type)
2074 		{
2075 		case INSERTION_EDIT:
2076 		  /* samp data snd chn */
2077 		  fprintf(fd, "%s" PROC_OPEN "%" print_mus_long PROC_SEP "%" print_mus_long PROC_SEP,
2078 			  to_proc_name(S_insert_samples_with_origin),
2079 			  ed->beg,
2080 			  ed->len);
2081 		  if (ed->origin)
2082 		    fprintf_with_possible_embedded_string(fd, ed->origin);
2083 		  else fprintf(fd, "\"%s\"", S_insert_samples);
2084 		  fprintf(fd, PROC_SEP);
2085 		  nfile = edit_list_data_to_temp_file(cp, ed, DONT_DELETE_ME, with_save_state_hook);
2086 		  fprintf(fd, "\"%s\"" PROC_SEP "sfile" PROC_SEP "%d", nfile, cp->chan);
2087 		  break;
2088 
2089 		case DELETION_EDIT:
2090 		  /* samp samps snd chn */
2091 		  fprintf(fd, "%s" PROC_OPEN "%" print_mus_long PROC_SEP "%" print_mus_long PROC_SEP "sfile" PROC_SEP "%d",
2092 			  to_proc_name(S_delete_samples),
2093 			  ed->beg,
2094 			  ed->len,
2095 			  cp->chan);
2096 		  break;
2097 
2098 		case CHANGE_EDIT:
2099 		  fprintf(fd, "%s" PROC_OPEN "%" print_mus_long PROC_SEP "%" print_mus_long PROC_SEP,
2100 			  to_proc_name(S_change_samples_with_origin),
2101 			  ed->beg,
2102 			  ed->len);
2103 		  if (ed->origin)
2104 		    fprintf_with_possible_embedded_string(fd, ed->origin);
2105 		  else fprintf(fd, "\"\"");
2106 		  fprintf(fd, PROC_SEP);
2107 		  nfile = edit_list_data_to_temp_file(cp, ed, DONT_DELETE_ME, with_save_state_hook);
2108 		  fprintf(fd, "\"%s\"" PROC_SEP "sfile" PROC_SEP "%d", nfile, cp->chan);
2109 		  break;
2110 
2111 		case EXTEND_EDIT:
2112 		  /* not currently saveable (this is a dummy edit fragment for zero-mix-drag position change) */
2113 		  break;
2114 
2115 		case SCALED_EDIT:
2116 		  fprintf(fd, "%s" PROC_SEP "sfile" PROC_SEP "%d",
2117 			  ed->origin, /* imports scaler */
2118 			  cp->chan);
2119 		  break;
2120 
2121 		case ZERO_EDIT:
2122 		  fprintf(fd, "%s" PROC_OPEN "%" print_mus_long PROC_SEP "%" print_mus_long PROC_SEP "sfile" PROC_SEP "%d",
2123 			  to_proc_name(S_pad_channel),
2124 			  ed->beg,
2125 			  ed->len,
2126 			  cp->chan);
2127 		  break;
2128 
2129 		case RAMP_EDIT:
2130 		  fprintf(fd, "%s" PROC_SEP "sfile" PROC_SEP "%d",
2131 			  ed->origin,
2132 			  cp->chan);
2133 		  break;
2134 
2135 		case MIX_EDIT:
2136 #if HAVE_SCHEME
2137 		  fprintf(fd, "(lambda (snd chn ignore) %s) sfile %d", ed->origin, cp->chan);
2138 #else
2139 		  fprintf(fd, "func = lambda do |snd, chn, ignore|\n        %s\n        end\n      func(sfile, %d", ed->origin, cp->chan);
2140 #endif
2141 		  break;
2142 
2143 		case CHANGE_MIX_EDIT:
2144 		  fprintf(fd, "begin %s", ed->origin);
2145 		  break;
2146 
2147 		default:
2148 		  snd_error("unknown edit branch: %s: %d %d",
2149 			    ed->origin,
2150 			    ed->edit_type,
2151 			    ed->sound_location);
2152 		  break;
2153 		}
2154 #endif
2155 	      if ((ed->edpos != AT_CURRENT_EDIT_POSITION) &&
2156 		  (ed->edpos != (i - 1)))
2157 		fprintf(fd, PROC_SEP " %d", ed->edpos);
2158 #if HAVE_RUBY
2159 	      else fprintf(fd, ", false");
2160 #endif
2161 #if HAVE_SCHEME
2162 	      else fprintf(fd, " #f");
2163 #endif
2164 #if HAVE_FORTH
2165 	      else
2166 		{
2167 		  if (!mix_ed)
2168 		    fprintf(fd, " #f");
2169 		}
2170 #endif
2171 	      if (nfile)
2172 		{
2173 #if HAVE_SCHEME
2174 		  fprintf(fd, " (list %d %" print_mus_long ")",
2175 			  (int)mus_sound_write_date(nfile),
2176 			  mus_sound_length(nfile));
2177 #endif
2178 #if HAVE_RUBY
2179  		  fprintf(fd, ", [%d, %" print_mus_long "]",
2180   			  (int)mus_sound_write_date(nfile),
2181   			  mus_sound_length(nfile));
2182 #endif
2183 #if HAVE_FORTH
2184 		  fprintf(fd, " '( %d %" print_mus_long " )",
2185 			  (int)mus_sound_write_date(nfile),
2186 			  mus_sound_length(nfile));
2187 #endif
2188 		  free(nfile);
2189 		}
2190 #if HAVE_FORTH
2191 	      if (mix_ed)
2192 		fprintf(fd, " %s\n", forth_func);
2193 	      else fprintf(fd, " %s drop\n", forth_func);
2194 #else
2195 	      fprintf(fd, ")\n"); /* works for both Ruby and Scheme */
2196 #endif
2197 	    }
2198 	}
2199     }
2200   if (cp->edit_ctr < edits)
2201 #if HAVE_RUBY
2202     fprintf(fd, "      undo(%d, sfile, %d);\n",
2203 	    edits - cp->edit_ctr,
2204 	    cp->chan);
2205 #endif
2206 #if HAVE_SCHEME
2207     fprintf(fd, "      (undo %d sfile %d)\n",
2208 	    edits - cp->edit_ctr,
2209 	    cp->chan);
2210 #endif
2211 #if HAVE_FORTH
2212     fprintf(fd, "      %d sfile %d undo drop\n",
2213 	    edits - cp->edit_ctr,
2214 	    cp->chan);
2215 #endif
2216     save_mark_list(fd, cp, false); /* false -> save just the current channel's marks */
2217 }
2218 
2219 
edit_list_to_function(chan_info * cp,int start_pos,int end_pos)2220 char *edit_list_to_function(chan_info *cp, int start_pos, int end_pos)
2221 {
2222 #if HAVE_SCHEME
2223   char *function = NULL, *old_function = NULL;
2224   bool close_mix_let = false;
2225   int i, edits;
2226 
2227   edits = cp->edit_ctr;
2228   while ((edits < (cp->edit_size - 1)) &&
2229 	 (cp->edits[edits + 1]))
2230     edits++;
2231 
2232   if ((end_pos > 0) &&    /* end_pos can be -1 = end of edits (?) */
2233       (end_pos < edits))
2234     edits = end_pos;
2235 
2236   if (start_pos > edits)
2237     return(mus_strdup("(lambda (snd chn) #f)"));
2238   if (start_pos == 0) start_pos = 1;
2239 
2240   if (channel_has_mixes(cp))
2241     {
2242       char *mix_list;
2243       mix_list = edit_list_mix_init(cp);
2244       if (mix_list)
2245 	{
2246 	  close_mix_let = true;
2247 	  function = mus_format("(lambda (snd chn)\n  (let (%s)", mix_list);
2248 	  free(mix_list);
2249 	}
2250       else function = mus_strdup("(lambda (snd chn)");
2251     }
2252   else function = mus_strdup("(lambda (snd chn)");
2253 
2254   for (i = start_pos; i <= edits; i++)
2255     {
2256       ed_list *ed;
2257       ed = cp->edits[i];
2258       if (ed)
2259 	{
2260 	  old_function = function;
2261 	  function = NULL;
2262 
2263 	  /* most of these depend on the caller to supply a usable re-call string (origin). */
2264 	  /*   In insert/change cases, there's basically no choice */
2265 	  if (ed->backed_up)
2266 	    {
2267 	      if ((ed->origin) &&
2268 		  (strncmp(ed->origin, "set!", 4) == 0))
2269 		function = mus_format("%s\n  (%s)", old_function, ed->origin);
2270 	      else function = mus_format("%s\n  (%s snd chn)", old_function, ed->origin);
2271 	    }
2272 	  else
2273 	    {
2274 	      switch (ed->edit_type)
2275 		{
2276 		case INSERTION_EDIT:
2277 		  /* this and change_edit are not bullet-proof -- there are many ways an incomplete
2278 		   *   origin can get here, but we want to trap the mix setters.  In save-state above,
2279 		   *   origin is just ignored, which is also less than ideal, but there are cases
2280 		   *   (map-channel for example) where the lambda form can't be saved correctly,
2281 		   *   so "the right thing" is not reachable.  Here, perhaps the strcmp should
2282 		   *   check for "set! -mix" or "set! (".
2283 		   */
2284 		  if ((!(ed->origin)) ||
2285 		      (strcmp(ed->origin, S_insert_samples) == 0))
2286 		    {
2287 		      /* save data in temp file, use insert-samples with file name */
2288 		      char *ofile;
2289 		      ofile = edit_list_data_to_temp_file(cp, ed, DELETE_ME, false);
2290 		      function = mus_format("%s\n  (%s %" print_mus_long " %" print_mus_long " \"%s\" snd chn)", old_function, S_insert_samples, ed->beg, ed->len, ofile);
2291 		      free(ofile);
2292 		    }
2293 		  else function = mus_format("%s\n  (%s snd chn)", old_function, ed->origin);
2294 		  break;
2295 
2296 		case CHANGE_EDIT:
2297 		  if ((!(ed->origin)) ||
2298 		      (strcmp(ed->origin, "set-samples") == 0))
2299 		    {
2300 		      /* save data in temp file, use set-samples with file name */
2301 		      char *ofile;
2302 		      ofile = edit_list_data_to_temp_file(cp, ed, DELETE_ME, false);
2303 		      function = mus_format("%s\n  (set-samples %" print_mus_long " %" print_mus_long " \"%s\" snd chn)", old_function, ed->beg, ed->len, ofile);
2304 		      free(ofile);
2305 		    }
2306 		  else
2307 		    {
2308 		      if (strncmp(ed->origin, "set!", 4) == 0)
2309 			function = mus_format("%s\n  (%s)", old_function, ed->origin);
2310 		      else function = mus_format("%s\n  (%s snd chn)", old_function, ed->origin);
2311 		    }
2312 		  break;
2313 
2314 		case DELETION_EDIT:
2315 		  function = mus_format("%s\n  (%s %" print_mus_long " %" print_mus_long " snd chn)", old_function, S_delete_samples, ed->beg, ed->len);
2316 		  break;
2317 
2318 		case SCALED_EDIT:
2319 		  function = mus_format("%s\n  (%s snd chn)", old_function, ed->origin);
2320 		  break;
2321 
2322 		case EXTEND_EDIT:
2323 		  /* mix drag case */
2324 		  break;
2325 
2326 		case RAMP_EDIT:
2327 		  function = mus_format("%s\n  (%s snd chn)", old_function, ed->origin);
2328 		  break;
2329 
2330 		case ZERO_EDIT:
2331 		  /* origin here is useless (see extend_with_zeros cases) */
2332 		  function = mus_format("%s\n  (%s %" print_mus_long " %" print_mus_long " snd chn)", old_function, S_pad_channel, ed->beg, ed->len);
2333 		  break;
2334 
2335 		case MIX_EDIT:
2336 		  function = mus_format("%s\n  %s", old_function, ed->origin);
2337 		  break;
2338 
2339 		case CHANGE_MIX_EDIT:
2340 		  function = mus_format("%s\n  %s", old_function, ed->origin);
2341 		  break;
2342 
2343 		default:
2344 		  break;
2345 		}
2346 	    }
2347 	  if (old_function) {free(old_function); old_function = NULL;}
2348 	}
2349     }
2350 
2351   old_function = function;
2352   if (close_mix_let)
2353     function = mus_format("%s))", old_function);
2354   else function = mus_format("%s)", old_function);
2355   free(old_function);
2356   return(function);
2357 #endif
2358 
2359 #if HAVE_RUBY
2360   char *function = NULL, *old_function = NULL;
2361   bool close_mix_let = false, first = true;
2362   int i, edits;
2363   edits = cp->edit_ctr;
2364   while ((edits < (cp->edit_size - 1)) &&
2365 	 (cp->edits[edits + 1]))
2366     edits++;
2367   if ((end_pos > 0) && (end_pos < edits)) edits = end_pos;
2368   if (start_pos > edits)
2369     return(mus_strdup("Proc.new {|snd, chn| false }"));
2370   if (channel_has_mixes(cp))
2371     {
2372       char *mix_list;
2373       mix_list = edit_list_mix_init(cp);
2374       if (mix_list)
2375 	{
2376 	  close_mix_let = true;
2377 	  function = mus_format("Proc.new {|snd, chn| %s; ", mix_list);
2378 	  free(mix_list);
2379 	}
2380       else function = mus_strdup("Proc.new {|snd, chn| ");
2381     }
2382   else function = mus_strdup("Proc.new {|snd, chn| ");
2383   for (i = start_pos; i <= edits; i++)
2384     {
2385       ed_list *ed;
2386       ed = cp->edits[i];
2387       if (ed)
2388 	{
2389 	  old_function = function;
2390 	  /* most of these depend on the caller to supply a usable re-call string (origin). */
2391 	  /*   In insert/change/ cases, there's basically no choice */
2392 	  if (ed->backed_up)
2393   	    {
2394  	      if ((ed->origin) &&
2395  		  (strncmp(ed->origin, "set_mix", 7) == 0))
2396   		function = mus_format("%s%s %s", function, (first) ? "" : ";", ed->origin);
2397  	      else function = mus_format("%s%s %s%ssnd, chn)",
2398  					 function,
2399  					 (first) ? "" : ";",
2400  					 ed->origin,
2401  					 (ed->origin[mus_strlen(ed->origin) - 1] == '(') ? "" : ", ");
2402   	    }
2403 	  else
2404 	    {
2405 	      switch (ed->edit_type)
2406 		{
2407 		case INSERTION_EDIT:
2408  		  if ((!(ed->origin)) ||
2409 		      (strcmp(ed->origin, to_proc_name(S_insert_samples)) == 0))
2410  		    {
2411  		      /* from HAVE_SCHEME above */
2412  		      /* save data in temp file, use insert-samples with file name */
2413  		      char *ofile;
2414  		      ofile = edit_list_data_to_temp_file(cp, ed, DELETE_ME, false);
2415  		      function = mus_format("%s %s(%" print_mus_long ", %" print_mus_long ", \"%s\", snd, chn)",
2416 					    function, to_proc_name(S_insert_samples), ed->beg, ed->len, ofile);
2417  		      free(ofile);
2418  		    }
2419  		  else function = mus_format("%s%s %s, snd, chn)", function, (first) ? "" : ";", ed->origin);
2420   		  break;
2421 
2422   		case CHANGE_EDIT:
2423  		  if ((!(ed->origin)) ||
2424 		      (strcmp(ed->origin, "set-samples") == 0))
2425  		    {
2426  		      /* from HAVE_SCHEME above */
2427  		      /* save data in temp file, use set-samples with file name */
2428  		      char *ofile;
2429  		      ofile = edit_list_data_to_temp_file(cp, ed, DELETE_ME, false);
2430  		      function = mus_format("%s set_samples(%" print_mus_long ", %" print_mus_long ", \"%s\", snd, chn)",
2431 					    function, ed->beg, ed->len, ofile);
2432  		      free(ofile);
2433  		    }
2434  		  else if ((ed->origin) &&
2435  			   (strncmp(ed->origin, "set_mix", 7) == 0))
2436  		    function = mus_format("%s%s %s", function, (first) ? "" : ";", ed->origin);
2437  		  else function = mus_format("%s%s %s%ssnd, chn)",
2438  					     function,
2439  					     (first) ? "" : ";",
2440  					     ed->origin,
2441  					     (ed->origin[mus_strlen(ed->origin) - 1] == '(') ? "" : ", ");
2442 		  break;
2443 
2444 		case DELETION_EDIT:
2445 		  function = mus_format("%s%s %s(%" print_mus_long ", %" print_mus_long ", snd, chn)",
2446 					function, (first) ? "" : ";", to_proc_name(S_delete_samples), ed->beg, ed->len);
2447 		  break;
2448 
2449 		case SCALED_EDIT:
2450 		  function = mus_format("%s%s %s, snd, chn)", function, (first) ? "" : ";", ed->origin);
2451 		  break;
2452 
2453 		case EXTEND_EDIT:
2454 		  /* mix drag case */
2455 		  break;
2456 
2457 		case RAMP_EDIT:
2458 		  function = mus_format("%s%s %s, snd, chn)", function, (first) ? "" : ";", ed->origin);
2459 		  break;
2460 
2461 		case ZERO_EDIT:
2462 		  /* origin here is useless (see extend_with_zeros cases) */
2463 		  function = mus_format("%s%s %s(%" print_mus_long ", %" print_mus_long ", snd, chn)",
2464 					function, (first) ? "" : ";", to_proc_name(S_pad_channel), ed->beg, ed->len);
2465 		  break;
2466 
2467 		case MIX_EDIT:
2468 		  function = mus_format("%s%s %s", function, (first) ? "" : ";", ed->origin);
2469 		  break;
2470 
2471 		case CHANGE_MIX_EDIT:
2472 		  function = mus_format("%s%s %s", function, (first) ? "" : ";", ed->origin);
2473 		  break;
2474 
2475 		default: break;
2476 		}
2477 	    }
2478 	  if (old_function) {free(old_function); old_function = NULL;}
2479 	}
2480       first = false;
2481     }
2482   old_function = function;
2483   if (close_mix_let)
2484     function = mus_format("%s }", function);
2485   else function = mus_format("%s }", function);
2486   free(old_function);
2487   return(function);
2488 #endif
2489 
2490 #if HAVE_FORTH
2491   char *function = NULL, *old_function = NULL;
2492   int i, edits;
2493   edits = cp->edit_ctr;
2494   while ((edits < (cp->edit_size - 1)) &&
2495 	 (cp->edits[edits + 1]))
2496     edits++;
2497   if ((end_pos > 0) && (end_pos < edits)) edits = end_pos;
2498   if (start_pos > edits)
2499     return(mus_strdup("lambda: <{ snd chn -- val }> #f ;"));
2500   if (channel_has_mixes(cp))
2501     {
2502       char *mix_list;
2503       mix_list = edit_list_mix_init(cp);
2504       if (mix_list)
2505 	{
2506 	  function = mus_format("lambda: <{ snd chn -- val }> %s", mix_list);
2507 	  free(mix_list);
2508 	}
2509       else function = mus_strdup("lambda: <{ snd chn -- val }>");
2510     }
2511   else function = mus_strdup("lambda: <{ snd chn -- val }>");
2512   for (i = start_pos; i <= edits; i++)
2513     {
2514       ed_list *ed;
2515       ed = cp->edits[i];
2516       if (ed)
2517 	{
2518 	  old_function = function;
2519 	  /* most of these depend on the caller to supply a usable re-call string (origin). */
2520 	  /*   In insert/change cases, there's basically no choice */
2521 	  if (ed->backed_up)
2522 	    {
2523 	      char *name, *func;
2524 	      func = split_origin(ed->origin, &name);
2525 	      if ((name) && (strncmp(name, "set-", 4) == 0))
2526 		function = mus_format("%s %s drop", function, ed->origin);
2527 	      else if ((ed->origin) && strstr(ed->origin, "mix-selection"))
2528 		function = mus_format("%s %s", function, ed->origin);
2529 	      else
2530 		{
2531 		  if (func)
2532 		    function = mus_format("%s %s snd chn %s drop", function, func, name);
2533 		  else function = mus_format("%s snd chn %s drop", function, name);
2534 		}
2535 	      if (func) free(func);
2536 	    }
2537 	  else
2538 	    {
2539 	      switch (ed->edit_type)
2540 		{
2541 		case CHANGE_EDIT:
2542 		  {
2543 		    char *name, *func;
2544 		    func = split_origin(ed->origin, &name);
2545 		    if ((name) && (strncmp(name, "set-", 4) == 0))
2546 		      function = mus_format("%s %s drop", function, ed->origin);
2547 		    else if ((ed->origin) && strstr(ed->origin, "mix-selection"))
2548 		      function = mus_format("%s %s", function, ed->origin);
2549 		    else
2550 		      {
2551 			if (func)
2552 			  function = mus_format("%s %s snd chn %s drop", function, func, name);
2553 			else function = mus_format("%s snd chn %s drop", function, name);
2554 		      }
2555 		    if (func) free(func);
2556 		  }
2557 		  break;
2558 		case DELETION_EDIT:
2559 		  function = mus_format("%s %" print_mus_long " %" print_mus_long " snd chn %s drop",
2560 					function, ed->beg, ed->len, S_delete_samples);
2561 		  break;
2562 		case INSERTION_EDIT:
2563 		case SCALED_EDIT:
2564 		case RAMP_EDIT:
2565 		  {
2566 		    char *name, *func;
2567 		    if ((func = split_origin(ed->origin, &name)))
2568 		      {
2569 			function = mus_format("%s %s snd chn %s drop", function, func, name);
2570 			free(func);
2571 		      }
2572 		    else function = mus_format("%s snd chn %s drop", function, name);
2573 		  }
2574 		  break;
2575 		case EXTEND_EDIT:
2576 		  /* mix drag case */
2577 		  break;
2578 		case ZERO_EDIT:
2579 		  /* origin here is unpredictable -- most of these extensions should be backed-over and invisible */
2580 		  /*   the one case that should survive (pad-channel) just passes its name as the origin */
2581 		  function = mus_format("%s %" print_mus_long " %" print_mus_long " snd chn %s drop",
2582 					function, ed->beg, ed->len, S_pad_channel);
2583 		  break;
2584 
2585 		case MIX_EDIT:
2586 		  function = mus_format("%s %s", function, ed->origin);
2587 		  break;
2588 
2589 		case CHANGE_MIX_EDIT:
2590 		  function = mus_format("%s %s", function, ed->origin);
2591 		  break;
2592 
2593 		default: break;
2594 		}
2595 	    }
2596 	  if (old_function) {free(old_function); old_function = NULL;}
2597 	}
2598     }
2599   old_function = function;
2600   function = mus_format("%s ;", function);
2601   free(old_function);
2602   return(function);
2603 #endif
2604 
2605 #if (!HAVE_EXTENSION_LANGUAGE)
2606   return(NULL);
2607 #endif
2608 }
2609 
2610 
2611 static ed_fragment *fragment_free_list = NULL;
2612 
make_ed_fragment(void)2613 static ed_fragment *make_ed_fragment(void)
2614 {
2615   if (fragment_free_list)
2616     {
2617       ed_fragment *e;
2618       e = fragment_free_list;
2619       fragment_free_list = (ed_fragment *)(fragment_free_list->next);
2620       memset((void *)e, 0, sizeof(ed_fragment));
2621       return(e);
2622     }
2623   return((ed_fragment *)calloc(1, sizeof(ed_fragment)));
2624 }
2625 
2626 
copy_fragment_mixes(ed_mixes * old_mixes)2627 static ed_mixes *copy_fragment_mixes(ed_mixes *old_mixes)
2628 {
2629   ed_mixes *ed;
2630   ed = (ed_mixes *)calloc(1, sizeof(ed_mixes));
2631   ed->size = old_mixes->size;
2632   ed->mix_list = (mix_state **)malloc(ed->size * sizeof(mix_state *));
2633   /* pointer fixup is in ripple, but we need access to the old choice */
2634   memcpy((void *)(ed->mix_list), (void *)(old_mixes->mix_list), ed->size * sizeof(mix_state *));
2635   return(ed);
2636 }
2637 
2638 
copy_fragment_ramps(ed_ramps * old_ramps)2639 static ed_ramps *copy_fragment_ramps(ed_ramps *old_ramps)
2640 {
2641   ed_ramps *new_ramps;
2642   new_ramps = (ed_ramps *)malloc(sizeof(ed_ramps));
2643   new_ramps->ramps = old_ramps->ramps;
2644   new_ramps->xramps = old_ramps->xramps;
2645   if (new_ramps->ramps > 0)
2646     {
2647       new_ramps->ramp_list = (ramp_state *)malloc(new_ramps->ramps * sizeof(ramp_state));
2648       memcpy((void *)(new_ramps->ramp_list), (void *)(old_ramps->ramp_list), new_ramps->ramps * sizeof(ramp_state));
2649     }
2650   else new_ramps->ramp_list = NULL;
2651   if (new_ramps->xramps > 0)
2652     {
2653       new_ramps->xramp_list = (xramp_state *)malloc(new_ramps->xramps * sizeof(xramp_state));
2654       memcpy((void *)(new_ramps->xramp_list), (void *)(old_ramps->xramp_list), new_ramps->xramps * sizeof(xramp_state));
2655     }
2656   else new_ramps->xramp_list = NULL;
2657   return(new_ramps);
2658 }
2659 
2660 
copy_ed_fragment(ed_fragment * new_ed,ed_fragment * old_ed)2661 static void copy_ed_fragment(ed_fragment *new_ed, ed_fragment *old_ed)
2662 {
2663   new_ed->typ = old_ed->typ;
2664   new_ed->snd = old_ed->snd;
2665   new_ed->out = old_ed->out;
2666   new_ed->beg = old_ed->beg;
2667   new_ed->end = old_ed->end;
2668   new_ed->scl = old_ed->scl;
2669 
2670   if (ED_RAMPS(old_ed))
2671     ED_RAMPS(new_ed) = copy_fragment_ramps(ED_RAMPS(old_ed));
2672 
2673   if (ED_MIXES(old_ed))
2674     ED_MIXES(new_ed) = copy_fragment_mixes(ED_MIXES(old_ed));
2675 }
2676 
2677 
copy_checked_ed_fragment(ed_fragment * new_ed,ed_fragment * old_ed)2678 static void copy_checked_ed_fragment(ed_fragment *new_ed, ed_fragment *old_ed)
2679 {
2680   /* insert list special case */
2681   if ((ED_RAMPS(old_ed)) &&
2682       (ED_RAMPS(new_ed)))
2683     {
2684       if (ED_RAMP_LIST(new_ed))	free(ED_RAMP_LIST(new_ed));
2685       if (ED_XRAMP_LIST(new_ed)) free(ED_XRAMP_LIST(new_ed));
2686       free(ED_RAMPS(new_ed));
2687     }
2688 
2689   if ((ED_MIXES(old_ed)) &&
2690       (ED_MIXES(new_ed)))
2691     {
2692       if (ED_MIX_LIST(new_ed)) free(ED_MIX_LIST(new_ed));
2693       free(ED_MIXES(new_ed));
2694     }
2695 
2696   copy_ed_fragment(new_ed, old_ed);
2697 }
2698 
2699 
clear_ed_fragment(ed_fragment * ed)2700 static void clear_ed_fragment(ed_fragment *ed)
2701 {
2702   /* used by free_ed_fragment and when zeroing a copied fragment */
2703 
2704   if (ED_RAMPS(ed))
2705     {
2706       if (ED_RAMP_LIST(ed)) free(ED_RAMP_LIST(ed));
2707       if (ED_XRAMP_LIST(ed)) free(ED_XRAMP_LIST(ed));
2708       free(ED_RAMPS(ed));
2709       ED_RAMPS(ed) = NULL;
2710     }
2711 
2712   if (ED_MIXES(ed))
2713     {
2714       if (ED_MIX_LIST(ed)) free(ED_MIX_LIST(ed));
2715       free(ED_MIXES(ed));
2716       ED_MIXES(ed) = NULL;
2717     }
2718 }
2719 
2720 
free_ed_fragment(ed_fragment * ed)2721 static ed_fragment *free_ed_fragment(ed_fragment *ed)
2722 {
2723   if (ed)
2724     {
2725       clear_ed_fragment(ed);
2726       ed->next = (struct ed_fragment *)fragment_free_list;
2727       fragment_free_list = ed;
2728     }
2729   return(NULL);
2730 }
2731 
2732 
make_ed_list(int size)2733 static ed_list *make_ed_list(int size)
2734 {
2735   ed_list *ed;
2736   int i;
2737   ed = (ed_list *)calloc(1, sizeof(ed_list));
2738 
2739   ed->size = size;
2740   ed->allocated_size = size;
2741   ed->fragments = (ed_fragment **)malloc(size * sizeof(ed_fragment *)); /* can't use malloc/free -- compiler dislikes the assignment in free */
2742   for (i = 0; i < size; i++)
2743     FRAGMENT(ed, i) = make_ed_fragment();
2744 
2745   ed->origin = NULL;
2746   ed->maxamp = -1.0;
2747   ed->maxamp_position = -1;
2748   ed->selection_maxamp = -1.0;
2749   ed->selection_maxamp_position = -1;
2750   ed->properties_gc_loc = NOT_A_GC_LOC;
2751   ed->properties = Xen_false;
2752   return(ed);
2753 }
2754 
2755 
set_ed_maxamp(chan_info * cp,int edpos,mus_float_t val)2756 void set_ed_maxamp(chan_info *cp, int edpos, mus_float_t val)
2757 {
2758   ed_list *ed;
2759   ed = cp->edits[edpos];
2760   ed->maxamp = val;
2761 }
2762 
2763 
ed_maxamp(chan_info * cp,int edpos)2764 mus_float_t ed_maxamp(chan_info *cp, int edpos)
2765 {
2766   ed_list *ed;
2767   ed = cp->edits[edpos];
2768   return(ed->maxamp);
2769 }
2770 
2771 
set_ed_maxamp_position(chan_info * cp,int edpos,mus_long_t val)2772 void set_ed_maxamp_position(chan_info *cp, int edpos, mus_long_t val)
2773 {
2774   ed_list *ed;
2775   ed = cp->edits[edpos];
2776   ed->maxamp_position = val;
2777 }
2778 
2779 
ed_maxamp_position(chan_info * cp,int edpos)2780 mus_long_t ed_maxamp_position(chan_info *cp, int edpos)
2781 {
2782   ed_list *ed;
2783   ed = cp->edits[edpos];
2784   return(ed->maxamp_position);
2785 }
2786 
2787 
set_ed_selection_maxamp(chan_info * cp,mus_float_t val)2788 void set_ed_selection_maxamp(chan_info *cp, mus_float_t val)
2789 {
2790   ed_list *ed;
2791   ed = cp->edits[cp->edit_ctr];
2792   ed->selection_maxamp = val;
2793 }
2794 
2795 
ed_selection_maxamp(chan_info * cp)2796 mus_float_t ed_selection_maxamp(chan_info *cp)
2797 {
2798   ed_list *ed;
2799   ed = cp->edits[cp->edit_ctr];
2800   return(ed->selection_maxamp);
2801 }
2802 
2803 
set_ed_selection_maxamp_position(chan_info * cp,mus_long_t val)2804 void set_ed_selection_maxamp_position(chan_info *cp, mus_long_t val)
2805 {
2806   ed_list *ed;
2807   ed = cp->edits[cp->edit_ctr];
2808   ed->selection_maxamp_position = val;
2809 }
2810 
2811 
ed_selection_maxamp_position(chan_info * cp)2812 mus_long_t ed_selection_maxamp_position(chan_info *cp)
2813 {
2814   ed_list *ed;
2815   ed = cp->edits[cp->edit_ctr];
2816   return(ed->selection_maxamp_position);
2817 }
2818 
2819 
2820 typedef struct {
2821   snd_fd **rds;
2822   int size;
2823 } sf_info;
2824 
free_ed_list(ed_list * ed,chan_info * cp)2825 static ed_list *free_ed_list(ed_list *ed, chan_info *cp)
2826 {
2827   if (ed)
2828     {
2829       if (FRAGMENTS(ed))
2830 	{
2831 	  int i;
2832 	  for (i = 0; i < ed->allocated_size; i++)
2833 	    free_ed_fragment(FRAGMENT(ed, i));
2834 	  free(FRAGMENTS(ed));
2835 	}
2836       if (ed->origin)
2837 	{
2838 	  free(ed->origin);
2839 	  ed->origin = NULL;
2840 	}
2841       if (ed->marks)
2842 	free_mark_list(ed);
2843       if (ed->peak_env)
2844 	ed->peak_env = free_peak_env_info(ed->peak_env);
2845       if (ed->fft)
2846 	ed->fft = free_enved_fft(ed->fft);
2847       if (ed->readers)
2848 	{
2849 	  int i;
2850 	  sf_info *lst;
2851 	  lst = (sf_info *)(ed->readers);
2852 	  for (i = 0; i < lst->size; i++)
2853 	    if (lst->rds[i])
2854 	      {
2855 		reader_out_of_data(lst->rds[i]);
2856 		lst->rds[i]->current_state = NULL; /* this pointer is now being freed, so it can't be safe to leave it around */
2857 		lst->rds[i]->cb = NULL;
2858 		lst->rds[i] = NULL;
2859 	      }
2860 	  free(lst->rds);
2861 	  free(lst);
2862 	  ed->readers = NULL;
2863 	}
2864       if (ed->properties_gc_loc != NOT_A_GC_LOC)
2865 	{
2866 	  snd_unprotect_at(ed->properties_gc_loc);
2867 	  ed->properties_gc_loc = NOT_A_GC_LOC;
2868 	  ed->properties = Xen_false;
2869 	}
2870       if (ED_MIXES(ed))
2871 	{
2872 	  free_ed_mixes(ED_MIXES(ed));
2873 	  ED_MIXES(ed) = NULL;
2874 	}
2875       free(ed);
2876     }
2877   return(NULL);
2878 }
2879 
2880 
backup_edit_list_1(chan_info * cp,bool freeing)2881 static void backup_edit_list_1(chan_info *cp, bool freeing)
2882 {
2883   int cur, i;
2884   ed_list *old_ed, *new_ed;
2885   mus_long_t old_end, new_end;
2886   ed_fragment *top = NULL;
2887 
2888   cur = cp->edit_ctr;
2889   if (cur <= 0) return;
2890   new_ed = cp->edits[cur];
2891   old_ed = cp->edits[cur - 1];
2892   new_ed->edpos = old_ed->edpos;
2893   new_ed->backed_up = true;
2894 
2895   old_end = old_ed->beg + old_ed->len;
2896   new_end = new_ed->beg + new_ed->len;
2897   if (old_end > new_end) new_end = old_end;
2898   if (old_ed->beg < new_ed->beg) new_ed->beg = old_ed->beg;
2899   new_ed->len = new_end - new_ed->beg + 1;
2900 
2901   if (freeing)
2902     top = fragment_free_list;
2903   free_ed_list(old_ed, cp);
2904   if (freeing)
2905     {
2906       while ((fragment_free_list) && (fragment_free_list != top))
2907 	{
2908 	  ed_fragment *e;
2909 	  e = fragment_free_list;
2910 	  fragment_free_list = e->next;
2911 	  free(e);
2912 	}
2913     }
2914 
2915   cp->edits[cur - 1] = new_ed;
2916   cp->edits[cur] = NULL;
2917   if (cp->sounds) /* protect from release_pending_sounds upon edit after undo after as-one-edit or whatever */
2918     for (i = 0; i < cp->sound_size; i++)
2919       {
2920 	snd_data *sd;
2921 	sd = cp->sounds[i];
2922 	if ((sd) && (sd->edit_ctr == cur)) sd->edit_ctr--;
2923       }
2924   cp->edit_ctr--;
2925   reflect_edit_history_change(cp);
2926 }
2927 
2928 
backup_edit_list(chan_info * cp)2929 void backup_edit_list(chan_info *cp)
2930 {
2931   backup_edit_list_1(cp, false);
2932 }
2933 
free_edit_list(chan_info * cp)2934 void free_edit_list(chan_info *cp)
2935 {
2936   if (cp)
2937     {
2938       if (cp->edits)
2939 	{
2940 	  int i;
2941 	  for (i = 0; i < cp->edit_size; i++)
2942 	    if (cp->edits[i])
2943 	      cp->edits[i] = free_ed_list(cp->edits[i], cp);
2944 	  free(cp->edits);
2945 	  cp->edits = NULL;
2946 	}
2947       cp->edit_ctr = -1;
2948       cp->edit_size = 0;
2949     }
2950 }
2951 
2952 
initial_ed_list(mus_long_t beg,mus_long_t end)2953 ed_list *initial_ed_list(mus_long_t beg, mus_long_t end)
2954 {
2955   ed_list *ed;
2956   ed = make_ed_list(2);
2957   ed->beg = beg;
2958   ed->len = end + 1;
2959   ed->selection_beg = NO_SELECTION;
2960   ed->selection_end = 0;
2961   ed->edit_type = INITIALIZE_EDIT;
2962   ed->sound_location = 0;
2963   ed->samples = end + 1;
2964 
2965   /* origin (channel %s %d) desc channel should be obvious from context */
2966   FRAGMENT_LOCAL_POSITION(ed, 0) = beg;
2967   FRAGMENT_LOCAL_END(ed, 0) = end;
2968   FRAGMENT_SCALER(ed, 0) = 1.0;
2969   FRAGMENT_TYPE(ed, 0) = ED_SIMPLE;
2970   if (ed->len > 0)
2971     {
2972       /* second block is our end-of-tree marker */
2973       FRAGMENT_SOUND(ed, 1) = EDIT_LIST_END_MARK;
2974       FRAGMENT_GLOBAL_POSITION(ed, 1) = end + 1;
2975     }
2976   else
2977     {
2978       FRAGMENT_SOUND(ed, 0) = EDIT_LIST_END_MARK;
2979       ed->size = 1;
2980     }
2981   return(ed);
2982 }
2983 
2984 
sound_is_silence(snd_info * sp)2985 snd_info *sound_is_silence(snd_info *sp)
2986 {
2987   if (sp)
2988     {
2989       uint32_t i;
2990       for (i = 0; i < sp->nchans; i++)
2991 	{
2992 	  chan_info *cp;
2993 	  ed_list *ed;
2994 	  cp = sp->chans[i];
2995 	  ed = cp->edits[0];
2996 	  FRAGMENT_SCALER(ed, 0) = 0.0;
2997 	  FRAGMENT_TYPE(ed, 0) = ED_ZERO;
2998 	}
2999     }
3000   return(sp);
3001 }
3002 
3003 
ensure_ed_ramps(ed_fragment * ed,int rmps,int xrmps)3004 static void ensure_ed_ramps(ed_fragment *ed, int rmps, int xrmps)
3005 {
3006   if (!(ED_RAMPS(ed)))
3007     {
3008       ED_RAMPS(ed) = (ed_ramps *)calloc(1, sizeof(ed_ramps));
3009       if (rmps > 0)
3010 	{
3011 	  ED_RAMP_LIST_SIZE(ed) = rmps;
3012 	  ED_RAMP_LIST(ed) = (ramp_state *)calloc(rmps, sizeof(ramp_state));
3013 	}
3014       if (xrmps > 0)
3015 	{
3016 	  ED_XRAMP_LIST_SIZE(ed) = xrmps;
3017 	  ED_XRAMP_LIST(ed) = (xramp_state *)calloc(xrmps, sizeof(xramp_state));
3018 	}
3019     }
3020   else
3021     {
3022       if (rmps > ED_RAMP_LIST_SIZE(ed))
3023 	{
3024 	  if (ED_RAMP_LIST_SIZE(ed) == 0)
3025 	    ED_RAMP_LIST(ed) = (ramp_state *)calloc(rmps, sizeof(ramp_state));
3026 	  else ED_RAMP_LIST(ed) = (ramp_state *)realloc(ED_RAMP_LIST(ed), rmps * sizeof(ramp_state));
3027 	  ED_RAMP_LIST_SIZE(ed) = rmps;
3028 
3029 	}
3030       if (xrmps > ED_XRAMP_LIST_SIZE(ed))
3031 	{
3032 	  if (ED_XRAMP_LIST_SIZE(ed) == 0)
3033 	    ED_XRAMP_LIST(ed) = (xramp_state *)calloc(xrmps, sizeof(xramp_state));
3034 	  else ED_XRAMP_LIST(ed) = (xramp_state *)realloc(ED_XRAMP_LIST(ed), xrmps * sizeof(xramp_state));
3035 	  ED_XRAMP_LIST_SIZE(ed) = xrmps;
3036 	}
3037     }
3038 }
3039 
3040 
new_before_ramp(ed_fragment * new_before,ed_fragment * old_before)3041 static void new_before_ramp(ed_fragment *new_before, ed_fragment *old_before)
3042 {
3043   if (ramp_op(ED_TYPE(old_before)))
3044     {
3045       int i, rmps, xrmps;
3046       rmps = ED_RAMP_LIST_SIZE(old_before);
3047       xrmps = ED_XRAMP_LIST_SIZE(old_before);
3048 
3049       ensure_ed_ramps(new_before, rmps, xrmps);
3050 
3051       for (i = 0; i < rmps; i++)
3052 	{
3053 	  ED_RAMP_INCR(new_before, i) = ED_RAMP_INCR(old_before, i);
3054 	  ED_RAMP_START(new_before, i) = ED_RAMP_START(old_before, i);
3055 	}
3056 
3057       for (i = 0; i < xrmps; i++)
3058 	{
3059 	  ED_XRAMP_OFFSET(new_before, i) = ED_XRAMP_OFFSET(old_before, i);
3060 	  ED_XRAMP_SCALER(new_before, i) = ED_XRAMP_SCALER(old_before, i);
3061 	  ED_XRAMP_INCR(new_before, i) = ED_XRAMP_INCR(old_before, i);
3062 	  ED_XRAMP_START(new_before, i) = ED_XRAMP_START(old_before, i);
3063 	}
3064     }
3065 }
3066 
3067 
new_after_ramp(ed_fragment * new_after,ed_fragment * old_after,mus_long_t samp)3068 static void new_after_ramp(ed_fragment *new_after, ed_fragment *old_after, mus_long_t samp)
3069 {
3070   mus_long_t dur;
3071   double d_dur;
3072 
3073   dur = samp - ED_GLOBAL_POSITION(old_after);
3074   d_dur = (double)dur;
3075 
3076   if (ramp_op(ED_TYPE(old_after)))
3077     {
3078       int i, rmps, xrmps;
3079       rmps = ED_RAMP_LIST_SIZE(old_after);
3080       xrmps = ED_XRAMP_LIST_SIZE(old_after);
3081 
3082       ensure_ed_ramps(new_after, rmps, xrmps);
3083 
3084       for (i = 0; i < rmps; i++)
3085 	{
3086 	  ED_RAMP_INCR(new_after, i) = ED_RAMP_INCR(old_after, i);
3087 	  ED_RAMP_START(new_after, i) = ED_RAMP_START(old_after, i) + ED_RAMP_INCR(old_after, i) * d_dur;
3088 	}
3089 
3090       for (i = 0; i < xrmps; i++)
3091 	{
3092 	  ED_XRAMP_OFFSET(new_after, i) = ED_XRAMP_OFFSET(old_after, i);
3093 	  ED_XRAMP_SCALER(new_after, i) = ED_XRAMP_SCALER(old_after, i);
3094 	  ED_XRAMP_INCR(new_after, i) = ED_XRAMP_INCR(old_after, i);
3095 	  ED_XRAMP_START(new_after, i) = ED_XRAMP_START(old_after, i) * exp(log(ED_XRAMP_INCR(old_after, i)) * d_dur);
3096 	}
3097     }
3098 }
3099 
3100 
3101 static void ripple_mixes(chan_info *cp, mus_long_t beg, mus_long_t change);
3102 static void ripple_mixes_with_scale(chan_info *cp, mus_long_t beg, mus_long_t len, mus_float_t scl);
3103 static ed_list *change_samples_in_list(mus_long_t beg, mus_long_t num, int pos, chan_info *cp, ed_fragment **rtn, const char *origin);
3104 
ripple_all(chan_info * cp,mus_long_t beg,mus_long_t samps)3105 static void ripple_all(chan_info *cp, mus_long_t beg, mus_long_t samps)
3106 {
3107   ripple_marks(cp, beg, samps);
3108   ripple_mixes(cp, beg, samps);
3109   check_for_first_edit(cp);
3110 }
3111 
3112 
lock_affected_mixes(chan_info * cp,int edpos,mus_long_t beg,mus_long_t end)3113 static bool lock_affected_mixes(chan_info *cp, int edpos, mus_long_t beg, mus_long_t end)
3114 {
3115   /* if a deletion, insertion, or change takes place on top of any part of
3116    *   a virtual mix, we have to write the mix+underlying stuff out as a
3117    *   change op.  This returns true if it changed the edit list.
3118    *
3119    * we assume this is called either just after prepare_edit_list so cp->edit_ctr
3120    *   points to the new (empty) edit list entry.
3121    */
3122 
3123   ed_list *ed;
3124   int i;
3125   bool changed = false;
3126   mus_long_t change_beg = -1, change_end = -1, possible_beg = -1, fragment_end;
3127 
3128   ed = cp->edits[edpos];
3129 
3130   /* first look for any directly affected mixes -- even if beg=0 and end=samples I think we want to
3131    *    optimize the change as much as possible.
3132    */
3133   for (i = 0; i < ed->size; i++)
3134     {
3135       mus_long_t fragment_beg;
3136       fragment_beg = FRAGMENT_GLOBAL_POSITION(ed, i);
3137       if (is_mix_op(FRAGMENT_TYPE(ed, i)))
3138 	{
3139 	  fragment_end = fragment_beg + FRAGMENT_LENGTH(ed, i);
3140 	  if (possible_beg < 0)
3141 	    possible_beg = fragment_beg;
3142 
3143 	  if ((fragment_beg <= end) &&
3144 	      (fragment_end >= beg) && /* hit a mix in the changing section */
3145 	      (change_beg < 0))
3146 	    {
3147 	      change_beg = possible_beg; /* this should track all the way back to where the current mixes started */
3148 	      change_end = fragment_end;
3149 	    }
3150 	}
3151       else
3152 	{
3153 	  possible_beg = -1; /* break current chain, if any */
3154 	  if (change_beg > 0)
3155 	    change_end = fragment_beg;
3156 	  if (fragment_beg > end) break;
3157 	}
3158     }
3159 
3160   if (change_beg >= 0)
3161     {
3162       /* now make the change edit, and make sure the affected mixes are removed from the mixes arrays */
3163       char *temp_file_name;
3164       io_error_t err;
3165       mus_long_t cur_len, cur_cursor;
3166 
3167       cur_len = ed->samples;
3168       cur_cursor = ed->cursor;
3169       temp_file_name = snd_tempnam();
3170       err = channel_to_file_with_bounds(cp, temp_file_name, edpos, change_beg, change_end - change_beg + 1, cp->sound->hdr, false);
3171       if (err == IO_NO_ERROR) /* else snd_error earlier? */
3172 	{
3173 	  file_info *hdr;
3174 	  hdr = make_file_info(temp_file_name, FILE_READ_ONLY, FILE_NOT_SELECTED);
3175 	  if (hdr)
3176 	    {
3177 	      int fd;
3178 	      ed_list *new_ed;
3179 	      ed_fragment *cb = NULL;
3180 	      bool full_file;
3181 
3182 	      full_file = ((change_beg == 0) &&
3183 			   (change_end >= ed->samples));
3184 
3185 	      fd = snd_open_read(temp_file_name);
3186 	      snd_file_open_descriptors(fd,
3187 					temp_file_name,
3188 					hdr->sample_type,
3189 					hdr->data_location,
3190 					hdr->chans,
3191 					hdr->type);
3192 	      if (full_file)
3193 		{
3194 		  new_ed = initial_ed_list(0, change_end);
3195 		  new_ed->origin = mus_strdup("lock mixes");
3196 		  new_ed->edpos = edpos;
3197 		  cb = FRAGMENT(new_ed, 0);
3198 		}
3199 	      else new_ed = change_samples_in_list(change_beg, change_end - change_beg + 1, edpos, cp, &cb, NULL);
3200 	      new_ed->edit_type = CHANGE_EDIT;
3201 	      new_ed->samples = cur_len;
3202 	      new_ed->cursor = cur_cursor;
3203 	      cp->edits[cp->edit_ctr] = new_ed;
3204 	      ED_SOUND(cb) = add_sound_file_to_edit_list(cp, temp_file_name,
3205 							 make_file_state(fd, hdr, 0, 0, FILE_BUFFER_SIZE),
3206 							 hdr, DELETE_ME, 0);
3207 	      new_ed->sound_location = ED_SOUND(cb);
3208 
3209 	      ripple_all(cp, 0, 0);
3210 	      reflect_mix_change(ANY_MIX_ID);
3211 	      changed = true;
3212 	    }
3213 	}
3214       if (temp_file_name) free(temp_file_name);
3215     }
3216   return(changed);
3217 }
3218 
3219 
3220 
3221 /* -------------------------------- insert samples -------------------------------- */
3222 
insert_section_into_list(mus_long_t samp,mus_long_t num,ed_list * current_state,ed_fragment ** rtn,const char * origin,mus_float_t scaler)3223 static ed_list *insert_section_into_list(mus_long_t samp, mus_long_t num, ed_list *current_state, ed_fragment **rtn, const char *origin, mus_float_t scaler)
3224 {
3225   int cur_len, cur_i, new_i;
3226   ed_fragment *new_f, *inserted_f = NULL;
3227   ed_list *new_state;
3228   if (num <= 0) return(NULL);
3229   cur_len = current_state->size;
3230   new_state = make_ed_list(cur_len + 3); /* leave room for possible split */
3231   for (cur_i = 0, new_i = 0; cur_i < cur_len; cur_i++, new_i++)
3232     {
3233       ed_fragment *cur_f;
3234       cur_f = FRAGMENT(current_state, cur_i);
3235       new_f = FRAGMENT(new_state, new_i);
3236       if (ED_GLOBAL_POSITION(cur_f) > samp)
3237 	{
3238 	  /* copy this fragment and ripple */
3239 	  copy_ed_fragment(new_f, cur_f);
3240 	  ED_GLOBAL_POSITION(new_f) += num;
3241 	}
3242       else
3243 	{
3244 	  if (ED_GLOBAL_POSITION(cur_f) == samp)
3245 	    {
3246 	      /* insert new fragment, copy to end */
3247 	      inserted_f = new_f;
3248 
3249 	      /* make newf and increment */
3250 	      new_i++;
3251 	      new_f = FRAGMENT(new_state, new_i);
3252 	      copy_ed_fragment(new_f, cur_f);
3253 	      ED_GLOBAL_POSITION(new_f) += num;
3254 	    }
3255 	  else
3256 	    {
3257 	      copy_ed_fragment(new_f, cur_f);
3258 	      /* look for splits */
3259 	      if (FRAGMENT_GLOBAL_POSITION(current_state, (cur_i + 1)) > samp)
3260 		{
3261 		  ed_fragment *split_front_f, *split_back_f;
3262 		  /* split current at samp */
3263 		  split_front_f = new_f;
3264 		  copy_checked_ed_fragment(split_front_f, cur_f);
3265 		  ED_LOCAL_END(split_front_f) = ED_LOCAL_POSITION(split_front_f) + samp - ED_GLOBAL_POSITION(split_front_f) - 1;
3266 		  /* samp - global position = where in current fragment, offset that by its local offset, turn into end sample */
3267 
3268 		  new_i++;
3269 		  inserted_f = FRAGMENT(new_state, new_i);
3270 		  /* deal with that later */
3271 
3272 		  new_i++;
3273 		  split_back_f = FRAGMENT(new_state, new_i);
3274 		  copy_ed_fragment(split_back_f, cur_f);
3275 		  ED_LOCAL_POSITION(split_back_f) = ED_LOCAL_END(split_front_f) + 1;
3276 		  ED_GLOBAL_POSITION(split_back_f) = samp + num; /* rippled */
3277 
3278 		  /* now fixup ramps affected by the split */
3279 		  if (ramp_op(ED_TYPE(cur_f)))
3280 		    {
3281 		      new_before_ramp(split_front_f, cur_f);
3282 		      new_after_ramp(split_back_f, cur_f, samp);
3283 		    }
3284 		}
3285 	    }
3286 	}
3287     }
3288   ED_GLOBAL_POSITION(inserted_f) = samp;
3289   ED_LOCAL_POSITION(inserted_f) = 0;
3290   ED_LOCAL_END(inserted_f) = num - 1;
3291   ED_TYPE(inserted_f) = ED_SIMPLE;
3292   ED_SCALER(inserted_f) = scaler;
3293   if (scaler == 0.0) ED_TYPE(inserted_f) = ED_ZERO;
3294   (*rtn) = inserted_f;
3295   new_state->size = new_i;
3296   new_state->beg = samp;
3297   new_state->len = num;
3298   if (origin) new_state->origin = mus_strdup(origin);
3299   return(new_state);
3300 }
3301 
3302 
insert_samples_into_list(mus_long_t samp,mus_long_t num,int pos,chan_info * cp,ed_fragment ** rtn,const char * origin,mus_float_t scaler)3303 static ed_list *insert_samples_into_list(mus_long_t samp, mus_long_t num, int pos, chan_info *cp, ed_fragment **rtn, const char *origin, mus_float_t scaler)
3304 {
3305   ed_list *new_state;
3306   new_state = insert_section_into_list(samp, num, cp->edits[pos], rtn, origin, scaler);
3307   new_state->edpos = pos;
3308   if ((cp->edits) && (cp->edit_ctr > 0))
3309     {
3310       ed_list *old_state;
3311       old_state = cp->edits[cp->edit_ctr - 1];
3312       new_state->selection_beg = old_state->selection_beg;
3313       new_state->selection_end = old_state->selection_end;
3314       new_state->cursor = old_state->cursor;
3315     }
3316   if (new_state->cursor > samp) new_state->cursor += num;
3317   return(new_state);
3318 }
3319 
3320 
insert_zeros(chan_info * cp,mus_long_t beg,mus_long_t num,int edpos)3321 static bool insert_zeros(chan_info *cp, mus_long_t beg, mus_long_t num, int edpos)
3322 {
3323   mus_long_t len, new_len;
3324   ed_fragment *cb;
3325   ed_list *ed, *old_ed;
3326   bool backup = false;
3327 
3328   old_ed = cp->edits[edpos];
3329   len = cp->edits[edpos]->samples;
3330   new_len = len + num;  /* we're inserting num zeros somewhere */
3331 
3332   if (lock_affected_mixes(cp, edpos, beg, beg))
3333     {
3334       edpos = cp->edit_ctr;
3335       old_ed = cp->edits[edpos];
3336       increment_edit_ctr(cp);
3337       backup = true;
3338     }
3339 
3340   ed = insert_samples_into_list(beg, num, edpos, cp, &cb, S_pad_channel, 0.0);
3341 
3342   ed->samples = new_len;
3343   ED_SOUND(cb) = EDIT_LIST_ZERO_MARK;
3344   ED_SCALER(cb) = 0.0;
3345   ED_TYPE(cb) = ED_ZERO;
3346   ed->edit_type = ZERO_EDIT;
3347   ed->sound_location = 0;
3348   ed->maxamp = old_ed->maxamp;
3349   ed->maxamp_position = old_ed->maxamp_position;
3350   if (ed->maxamp_position >= beg)
3351     ed->maxamp_position += num;
3352   cp->edits[cp->edit_ctr] = ed;
3353   peak_env_insert_zeros(cp, beg, num, edpos);
3354 
3355   ripple_all(cp, beg, num);
3356   ripple_selection(ed, beg, num);
3357 
3358   reflect_sample_change_in_axis(cp);
3359   reflect_mix_change(ANY_MIX_ID);
3360   after_edit(cp);
3361 
3362   if (backup)
3363     backup_edit_list(cp);
3364 
3365   return(true);
3366 }
3367 
3368 
extend_with_zeros(chan_info * cp,mus_long_t beg,mus_long_t num,int edpos,const char * origin)3369 bool extend_with_zeros(chan_info *cp, mus_long_t beg, mus_long_t num, int edpos, const char *origin)
3370 {
3371   /* this can also be called when beg is within the current sound -> insert a block of zeros */
3372 
3373   int i;
3374   mus_long_t len, new_len;
3375   ed_fragment *cb;
3376   ed_list *new_ed, *old_ed;
3377 
3378   if (num <= 0) return(true); /* false if can't edit, but this is a no-op */
3379 
3380   if (!(prepare_edit_list(cp, edpos, origin)))
3381     return(false);
3382 
3383   old_ed = cp->edits[edpos];
3384   len = cp->edits[edpos]->samples;
3385 
3386   /* check for insert zeros case */
3387   if (beg < len)
3388     return(insert_zeros(cp, beg, num, edpos));
3389 
3390   /* extend with zeros at end */
3391   new_len = beg + num; /* beg might even be > current end? */
3392   beg = len;
3393   num = new_len - beg;
3394 
3395   new_ed = make_ed_list(old_ed->size + 1);
3396   new_ed->beg = beg;
3397   new_ed->len = num;
3398   new_ed->samples = new_len;
3399   new_ed->cursor = old_ed->cursor;
3400   new_ed->origin = mus_strdup(origin);
3401   new_ed->edpos = edpos;
3402   new_ed->selection_beg = old_ed->selection_beg;
3403   new_ed->selection_end = old_ed->selection_end;
3404   new_ed->maxamp = old_ed->maxamp;
3405   new_ed->maxamp_position = old_ed->maxamp_position;
3406 
3407   for (i = 0; i < old_ed->size; i++)
3408     copy_ed_fragment(FRAGMENT(new_ed, i), FRAGMENT(old_ed, i));
3409 
3410   /* make the zero fragment, fixup the end fragment */
3411   if (FRAGMENT(new_ed, old_ed->size)) free_ed_fragment(FRAGMENT(new_ed, old_ed->size)); /* make room for extension */
3412   FRAGMENT(new_ed, old_ed->size) = FRAGMENT(new_ed, old_ed->size - 1);
3413   FRAGMENT_GLOBAL_POSITION(new_ed, old_ed->size) = new_len;
3414 
3415   cb = make_ed_fragment();
3416 
3417   FRAGMENT(new_ed, old_ed->size - 1) = cb;
3418   ED_SOUND(cb) = EDIT_LIST_ZERO_MARK;
3419   ED_SCALER(cb) = 0.0;
3420   ED_TYPE(cb) = ED_ZERO;
3421   ED_GLOBAL_POSITION(cb) = beg;
3422   ED_LOCAL_POSITION(cb) = 0;
3423   ED_LOCAL_END(cb) = num - 1;
3424   new_ed->edit_type = ZERO_EDIT;
3425   cp->edits[cp->edit_ctr] = new_ed;
3426   peak_env_insert_zeros(cp, beg, num, edpos);
3427 
3428   ripple_all(cp, 0, 0);
3429   reflect_sample_change_in_axis(cp);
3430   after_edit(cp);
3431   return(true);
3432 }
3433 
3434 
file_insert_samples(mus_long_t beg,mus_long_t num,const char * inserted_file,chan_info * cp,int chan,file_delete_t auto_delete,const char * origin,int edpos)3435 bool file_insert_samples(mus_long_t beg, mus_long_t num, const char *inserted_file, chan_info *cp, int chan, file_delete_t auto_delete, const char *origin, int edpos)
3436 {
3437   mus_long_t len;
3438   ed_fragment *cb;
3439   file_info *hdr;
3440   ed_list *ed, *old_ed;
3441   int backup = 0;
3442 
3443   old_ed = cp->edits[edpos];
3444   len = old_ed->samples;
3445   if (beg > len)
3446     {
3447       if (!(extend_with_zeros(cp, len, beg - len, edpos, origin)))
3448 	return(false);
3449       edpos = cp->edit_ctr;
3450       len = current_samples(cp);
3451       backup++;
3452     }
3453   if (!(prepare_edit_list(cp, edpos, origin)))
3454     return(false);
3455 
3456   if (lock_affected_mixes(cp, edpos, beg, beg))
3457     {
3458       edpos = cp->edit_ctr;
3459       increment_edit_ctr(cp);
3460       backup++;
3461     }
3462 
3463   ed = insert_samples_into_list(beg, num, edpos, cp, &cb, origin, 1.0);
3464   ed->samples = len + num;
3465   ed->edit_type = INSERTION_EDIT;
3466   cp->edits[cp->edit_ctr] = ed;
3467 
3468   if ((old_ed->maxamp_position != -1) &&
3469       (mus_sound_channel_maxamp_exists(inserted_file, chan)))
3470     {
3471       mus_float_t mx;
3472       mus_long_t pos;
3473       mx = mus_sound_channel_maxamp(inserted_file, chan, &pos);
3474       if (mx > old_ed->maxamp)
3475 	{
3476 	  ed->maxamp = mx;
3477 	  ed->maxamp_position = beg + pos;
3478 	}
3479       else
3480 	{
3481 	  ed->maxamp = old_ed->maxamp;
3482 	  ed->maxamp_position = old_ed->maxamp_position;
3483 	  if (ed->maxamp_position >= beg)
3484 	    ed->maxamp_position += num;
3485 	}
3486     }
3487 
3488   hdr = make_file_info(inserted_file, FILE_READ_ONLY, FILE_NOT_SELECTED);
3489   if (hdr)
3490     {
3491       int fd;
3492       fd = snd_open_read(inserted_file);
3493       snd_file_open_descriptors(fd,
3494 				inserted_file,
3495 				hdr->sample_type,
3496 				hdr->data_location,
3497 				hdr->chans,
3498 				hdr->type);
3499       during_open(fd, inserted_file, SND_INSERT_FILE);
3500       ED_SOUND(cb) = add_sound_file_to_edit_list(cp, inserted_file,
3501 						 make_file_state(fd, hdr, chan, 0, FILE_BUFFER_SIZE),
3502 						 hdr, auto_delete, chan);
3503       ed->sound_location = ED_SOUND(cb);
3504 
3505       /* mixes are rippled first (ripple_all -> ripple_mixes)
3506        *   so the lock should affect those that were split by the insertion, which in current
3507        *   terms means we're interested only in 'beg'
3508        */
3509       ripple_all(cp, beg, num);
3510       ripple_selection(ed, beg, num);
3511       reflect_sample_change_in_axis(cp);
3512       reflect_mix_change(ANY_MIX_ID);
3513       after_edit(cp);
3514 
3515       if (backup > 0)
3516 	{
3517 	  backup_edit_list(cp);
3518 	  if (backup > 1)
3519 	    backup_edit_list(cp);
3520 	}
3521 
3522       return(true);
3523     }
3524   return(false);
3525 }
3526 
3527 
3528 #define MAXAMP_CHECK_SIZE 10000
3529 /* making this larger was slower, smaller no difference? */
3530 
insert_samples(mus_long_t beg,mus_long_t num,mus_float_t * vals,chan_info * cp,const char * origin,int edpos)3531 bool insert_samples(mus_long_t beg, mus_long_t num, mus_float_t *vals, chan_info *cp, const char *origin, int edpos)
3532 {
3533   mus_long_t len;
3534   ed_fragment *cb;
3535   ed_list *ed, *old_ed;
3536   int backup = 0;
3537 
3538   if (num <= 0) return(true);
3539   old_ed = cp->edits[edpos];
3540   len = old_ed->samples;
3541   if (beg > len)
3542     {
3543       if (!(extend_with_zeros(cp, len, beg - len, edpos, origin)))
3544 	return(false);
3545       edpos = cp->edit_ctr;
3546       len = current_samples(cp);
3547       backup++;
3548     }
3549 
3550   if (!(prepare_edit_list(cp, edpos, origin)))
3551     return(false);
3552 
3553   if (lock_affected_mixes(cp, edpos, beg, beg))
3554     {
3555       edpos = cp->edit_ctr;
3556       increment_edit_ctr(cp);
3557       backup++;
3558     }
3559 
3560   ed = insert_samples_into_list(beg, num, edpos, cp, &cb, origin, 1.0);
3561   ed->edit_type = INSERTION_EDIT;
3562   ed->samples = len + num;
3563   cp->edits[cp->edit_ctr] = ed;
3564 
3565   prepare_sound_list(cp);
3566   cp->sounds[cp->sound_ctr] = make_snd_data_buffer(vals, (int)num, cp->edit_ctr);
3567   ED_SOUND(cb) = cp->sound_ctr;
3568   ed->sound_location = ED_SOUND(cb);
3569 
3570   if ((old_ed->maxamp_position != -1) &&
3571       (num < MAXAMP_CHECK_SIZE))
3572     {
3573       mus_float_t mx;
3574       int i, pos = 0;
3575       mx = fabs(vals[0]);
3576       for (i = 1; i < num; i++)
3577 	{
3578 	  mus_float_t temp;
3579 	  temp = fabs(vals[i]);
3580 	  if (temp > mx)
3581 	    {
3582 	      mx = temp;
3583 	      pos = i;
3584 	    }
3585 	}
3586       if (mx > old_ed->maxamp)
3587 	{
3588 	  ed->maxamp = mx;
3589 	  ed->maxamp_position = beg + pos;
3590 	}
3591       else
3592 	{
3593 	  ed->maxamp = old_ed->maxamp;
3594 	  ed->maxamp_position = old_ed->maxamp_position;
3595 	  if (ed->maxamp_position >= beg)
3596 	    ed->maxamp_position += num;
3597 	}
3598     }
3599 
3600   ripple_all(cp, beg, num);
3601   ripple_selection(ed, beg, num);
3602   reflect_sample_change_in_axis(cp);
3603   reflect_mix_change(ANY_MIX_ID);
3604   after_edit(cp);
3605 
3606   if (backup > 0)
3607     {
3608       backup_edit_list(cp);
3609       if (backup > 1)
3610 	backup_edit_list(cp);
3611     }
3612 
3613   return(true);
3614 }
3615 
3616 
insert_complete_file(snd_info * sp,const char * str,mus_long_t chan_beg,file_delete_t auto_delete)3617 bool insert_complete_file(snd_info *sp, const char *str, mus_long_t chan_beg, file_delete_t auto_delete)
3618 {
3619   int nc;
3620   bool ok = false;
3621   char *filename;
3622   filename = mus_expand_filename(str);
3623   nc = mus_sound_chans(filename);
3624   if (nc > 0)
3625     {
3626       mus_long_t len;
3627       len = mus_sound_framples(filename);
3628       if (len == 0)
3629 	snd_warning("%s has no data", str);
3630       else
3631 	{
3632 	  int i, j, first_chan = 0;
3633 	  chan_info *ncp;
3634 	  if (sp->sync != 0)
3635 	    ncp = sp->chans[0];
3636 	  else ncp = any_selected_channel(sp);
3637 	  first_chan = ncp->chan;
3638 	  for (i = first_chan, j = 0; (j < nc) && (i < (int)sp->nchans); i++, j++)
3639 	    {
3640 	      char *origin;
3641 	      ncp = sp->chans[i];
3642 #if HAVE_FORTH
3643 	      origin = mus_format("\"%s\" %" print_mus_long " %d %s drop",
3644 				  filename, chan_beg, j, S_insert_sound);
3645 #else
3646 	      origin = mus_format("%s" PROC_OPEN "\"%s\"" PROC_SEP "%" print_mus_long PROC_SEP "%d",
3647 				  to_proc_name(S_insert_sound), filename, chan_beg, j);
3648 #endif
3649 	      ok = file_insert_samples(chan_beg, len, filename, ncp, j, auto_delete, origin, ncp->edit_ctr);
3650 	      if (ok)
3651 		update_graph(ncp);
3652 	      free(origin);
3653 	    }
3654 	}
3655     }
3656   else snd_warning("can't read %s", str);
3657   free(filename);
3658   return(ok);
3659 }
3660 
3661 
insert_complete_file_at_cursor(snd_info * sp,const char * filename)3662 bool insert_complete_file_at_cursor(snd_info *sp, const char *filename)
3663 {
3664   chan_info *ncp;
3665   ncp = any_selected_channel(sp);
3666   return(insert_complete_file(sp, filename, cursor_sample(ncp), DONT_DELETE_ME));
3667 }
3668 
3669 
3670 
3671 /* -------------------------------- delete samples -------------------------------- */
3672 
delete_section_from_list(mus_long_t beg,mus_long_t num,ed_list * current_state)3673 static ed_list *delete_section_from_list(mus_long_t beg, mus_long_t num, ed_list *current_state)
3674 {
3675   int cur_len, cur_i, new_i;
3676   ed_fragment *new_f;
3677   mus_long_t end, next_pos;
3678   ed_list *new_state;
3679   if (num <= 0) return(NULL);
3680   cur_len = current_state->size;
3681   end = beg + num;
3682   new_state = make_ed_list(cur_len + 3); /* leave room for possible splits */
3683   for (cur_i = 0, new_i = 0; cur_i < cur_len; cur_i++)
3684     {
3685       ed_fragment *cur_f;
3686       cur_f = FRAGMENT(current_state, cur_i);
3687       new_f = FRAGMENT(new_state, new_i);
3688       if (ED_GLOBAL_POSITION(cur_f) >= end)
3689 	{
3690 	  /* copy this fragment (we're past the deletion) */
3691 	  copy_ed_fragment(new_f, cur_f);
3692 	  ED_GLOBAL_POSITION(new_f) -= num;
3693 	  new_i++;
3694 	}
3695       else
3696 	{
3697 	  next_pos = FRAGMENT_GLOBAL_POSITION(current_state, (cur_i + 1));
3698 	  if (next_pos <= beg)
3699 	    {
3700 	      /* we're before deletion without any split, just copy */
3701 	      copy_ed_fragment(new_f, cur_f);
3702 	      new_i++;
3703 	    }
3704 	  else
3705 	    {
3706 	      /* split off begin (if any), delete until num used up, split off end (if any) */
3707 	      /* if global_pos > beg and global_pos next <= end, just drop it, else split */
3708 	      if (ED_GLOBAL_POSITION(cur_f) < beg)
3709 		{
3710 		  ed_fragment *split_front_f;
3711 		  /* split front */
3712 		  split_front_f = new_f;
3713 		  copy_ed_fragment(split_front_f, cur_f);
3714 		  new_i++;
3715 		  ED_LOCAL_END(split_front_f) = ED_LOCAL_POSITION(split_front_f) + beg - ED_GLOBAL_POSITION(split_front_f) - 1;
3716 		  /* samp - global position = where in current fragment, offset that by its local offset, turn into end sample */
3717 		  if (ramp_op(ED_TYPE(cur_f)))
3718 		    new_before_ramp(split_front_f, cur_f);
3719 		}
3720 	      next_pos = FRAGMENT_GLOBAL_POSITION(current_state, (cur_i + 1));
3721 	      if (next_pos > end)
3722 		{
3723 		  ed_fragment *split_back_f;
3724 		  new_f = FRAGMENT(new_state, new_i);
3725 		  split_back_f = new_f;
3726 		  copy_ed_fragment(split_back_f, cur_f);
3727 		  new_i++;
3728 		  ED_GLOBAL_POSITION(split_back_f) = beg;
3729 		  ED_LOCAL_POSITION(split_back_f) += end - ED_GLOBAL_POSITION(cur_f);
3730 		  if (ramp_op(ED_TYPE(cur_f)))
3731 		    new_after_ramp(split_back_f, cur_f, end);
3732 		}
3733 	    }
3734 	}
3735     }
3736   new_state->size = new_i;
3737   new_state->beg = beg;
3738   new_state->len = num;
3739 #if HAVE_FORTH
3740   new_state->origin = mus_format("%" print_mus_long " %" print_mus_long " %s drop", beg, num, S_delete_samples);
3741 #else
3742 #if HAVE_RUBY
3743   {
3744     char *temp;
3745     temp = to_proc_name(S_delete_samples);
3746     new_state->origin = mus_format("%s" PROC_OPEN "%" print_mus_long PROC_SEP "%" print_mus_long, temp, beg, num);
3747     if (temp) free(temp);
3748   }
3749 #else
3750   new_state->origin = mus_format("%s" PROC_OPEN "%" print_mus_long PROC_SEP "%" print_mus_long, to_proc_name(S_delete_samples), beg, num);
3751 #endif
3752 #endif
3753   new_state->edit_type = DELETION_EDIT;
3754   new_state->sound_location = 0;
3755   return(new_state);
3756 }
3757 
3758 
delete_samples(mus_long_t beg,mus_long_t num,chan_info * cp,int edpos)3759 bool delete_samples(mus_long_t beg, mus_long_t num, chan_info *cp, int edpos)
3760 {
3761   mus_long_t len;
3762 
3763   if (num <= 0) return(true);
3764   len = cp->edits[edpos]->samples;
3765 
3766   if ((beg < len) && (beg >= 0))
3767     {
3768       ed_list *ed;
3769       bool backup = false, read_max = false;
3770 
3771       if ((beg + num) > len) num = len - beg;
3772       if (!(prepare_edit_list(cp, edpos, S_delete_samples)))
3773 	return(false);
3774 
3775       if (lock_affected_mixes(cp, edpos, beg, beg + num))
3776 	{
3777 	  edpos = cp->edit_ctr;
3778 	  increment_edit_ctr(cp);
3779 	  backup = true;
3780 	}
3781 
3782       ed = delete_section_from_list(beg, num, cp->edits[edpos]);
3783       if ((cp->edits) && (cp->edit_ctr > 0))
3784 	{
3785 	  ed_list *old_state;
3786 	  old_state = cp->edits[cp->edit_ctr - 1];
3787 	  ed->selection_beg = old_state->selection_beg;
3788 	  ed->selection_end = old_state->selection_end;
3789 	  ed->cursor = old_state->cursor;
3790 
3791 	  if (((old_state->maxamp_position >= 0) && (old_state->maxamp_position < beg)) ||
3792 	      (old_state->maxamp_position > (beg + num)))
3793 	    {
3794 	      ed->maxamp = old_state->maxamp;
3795 	      ed->maxamp_position = old_state->maxamp_position;
3796 	      if (old_state->maxamp_position > (beg + num))
3797 		ed->maxamp_position -= num;
3798 	    }
3799 	  else
3800 	    {
3801 	      if ((beg == 0) && (num >= len))
3802 		{
3803 		  ed->maxamp = 0.0;
3804 		  ed->maxamp_position = 0;
3805 		}
3806 	      else read_max = ((len - num) < MAXAMP_CHECK_SIZE);
3807 	    }
3808 	}
3809       ed->edpos = edpos;
3810       ed->samples = len - num;
3811       cp->edits[cp->edit_ctr] = ed;
3812 
3813       ripple_all(cp, beg, -num);
3814       ripple_selection(ed, beg, -num);
3815       if (ed->cursor > beg)
3816 	{
3817 	  /* this added 6-Dec-02 */
3818 	  ed->cursor -= num;
3819 	  if (ed->cursor < beg) ed->cursor = beg;
3820 	}
3821 
3822       if (read_max)
3823 	{
3824 	  mus_long_t new_len;
3825 	  mus_float_t mx;
3826 	  int i, loc = 0;
3827 	  snd_fd *sf;
3828 	  new_len = ed->samples;
3829 
3830 	  sf = init_sample_read_any_with_bufsize(0, cp, READ_FORWARD, cp->edit_ctr, new_len + 1); /* read current samps */
3831 	  sampler_set_safe(sf, new_len);
3832 	  mx = fabs(read_sample(sf));
3833 	  for (i = 1; i < new_len; i++)
3834 	    {
3835 	      mus_float_t temp;
3836 	      temp = fabs(read_sample(sf));
3837 	      if (temp > mx)
3838 		{
3839 		  mx = temp;
3840 		  loc = i;
3841 		}
3842 	    }
3843 	  free_snd_fd(sf);
3844 	  ed->maxamp = mx;
3845 	  ed->maxamp_position = loc;
3846 	}
3847 
3848       reflect_sample_change_in_axis(cp);
3849       reflect_mix_change(ANY_MIX_ID);
3850       after_edit(cp);
3851 
3852       if (backup)
3853 	backup_edit_list(cp);
3854       return(true);
3855     }
3856   return(false);
3857 }
3858 
3859 
3860 
3861 /* -------------------------------- change samples -------------------------------- */
3862 
change_samples_in_list(mus_long_t beg,mus_long_t num,int pos,chan_info * cp,ed_fragment ** rtn,const char * origin)3863 static ed_list *change_samples_in_list(mus_long_t beg, mus_long_t num, int pos, chan_info *cp, ed_fragment **rtn, const char *origin)
3864 {
3865   /* delete + insert -- already checked that beg < cur end */
3866   ed_list *new_state;
3867   mus_long_t del_num, cur_end;
3868   ed_fragment *changed_f;
3869 
3870   if (num <= 0) return(NULL);
3871 
3872   cur_end = cp->edits[pos]->samples;
3873   del_num = cur_end - beg;
3874   if (num < del_num) del_num = num;
3875   if (del_num > 0)
3876     {
3877       ed_list *del_state;
3878       del_state = delete_section_from_list(beg, del_num, cp->edits[pos]);
3879       new_state = insert_section_into_list(beg, num, del_state, &changed_f, origin, 1.0);
3880       free_ed_list(del_state, cp);
3881     }
3882   else new_state = insert_section_into_list(beg, num, cp->edits[pos], &changed_f, origin, 1.0);
3883 
3884   (*rtn) = changed_f;
3885 
3886   if ((cp->edits) && (cp->edit_ctr > 0))
3887     {
3888       ed_list *old_state;
3889       old_state = cp->edits[cp->edit_ctr - 1];
3890       new_state->selection_beg = old_state->selection_beg;
3891       new_state->selection_end = old_state->selection_end;
3892     }
3893   new_state->edpos = pos;
3894 
3895   return(new_state);
3896 }
3897 
3898 
file_change_samples(mus_long_t beg,mus_long_t num,const char * tempfile,chan_info * cp,int chan,file_delete_t auto_delete,const char * origin,int edpos)3899 bool file_change_samples(mus_long_t beg, mus_long_t num, const char *tempfile, chan_info *cp, int chan, file_delete_t auto_delete, const char *origin, int edpos)
3900 {
3901   file_info *hdr;
3902   hdr = make_file_info(tempfile, FILE_READ_ONLY, FILE_NOT_SELECTED);
3903   if (hdr)
3904     {
3905       ed_list *ed, *old_ed;
3906       mus_long_t prev_len, new_len;
3907       ed_fragment *cb = NULL;
3908       int fd;
3909       int backup = 0;
3910 
3911       old_ed = cp->edits[edpos];
3912       prev_len = old_ed->samples;
3913       if (beg > prev_len)
3914 	{
3915 	  if (!(extend_with_zeros(cp, prev_len, beg - prev_len, edpos, origin)))
3916 	    {
3917 	      free_file_info(hdr);
3918 	      return(false);
3919 	    }
3920 	  backup++;
3921 	  edpos = cp->edit_ctr;
3922 	  prev_len = current_samples(cp);
3923 	}
3924       new_len = beg + num;
3925       if (new_len < prev_len) new_len = prev_len;
3926       if (!(prepare_edit_list(cp, edpos, origin)))
3927 	{
3928 	  free_file_info(hdr);
3929 	  return(false);
3930 	}
3931       if (lock_affected_mixes(cp, edpos, beg, beg + num))
3932 	{
3933 	  edpos = cp->edit_ctr;
3934 	  increment_edit_ctr(cp);
3935 	  backup++;
3936 	}
3937 
3938       ed = change_samples_in_list(beg, num, edpos, cp, &cb, origin);
3939       ed->edit_type = CHANGE_EDIT;
3940       ed->samples = new_len;
3941       if (cp->edit_ctr > 0) ed->cursor = cp->edits[cp->edit_ctr - 1]->cursor;
3942       cp->edits[cp->edit_ctr] = ed;
3943 
3944       if (((old_ed->maxamp_position >= 0) ||
3945 	   ((beg == 0) && (num >= old_ed->samples))) &&
3946 	  (mus_sound_channel_maxamp_exists(tempfile, chan)))
3947 	{
3948 	  mus_float_t mx;
3949 	  mus_long_t pos;
3950 	  mx = mus_sound_channel_maxamp(tempfile, chan, &pos);
3951 	  if ((mx > old_ed->maxamp) ||
3952 	      ((beg == 0) && (num >= old_ed->samples)))
3953 	    {
3954 	      ed->maxamp = mx;
3955 	      ed->maxamp_position = beg + pos;
3956 	    }
3957 	  else
3958 	    {
3959 	      /* make sure old max info is still relevant */
3960 	      if ((old_ed->maxamp_position < beg) ||
3961 		  (old_ed->maxamp_position > (beg + num)))
3962 		{
3963 		  ed->maxamp = old_ed->maxamp;
3964 		  ed->maxamp_position = old_ed->maxamp_position;
3965 		}
3966 	    }
3967 	}
3968 
3969       fd = snd_open_read(tempfile);
3970       snd_file_open_descriptors(fd,
3971 				tempfile,
3972 				hdr->sample_type,
3973 				hdr->data_location,
3974 				hdr->chans,
3975 				hdr->type);
3976       during_open(fd, tempfile, SND_CHANGE_FILE);
3977       ED_SOUND(cb) = add_sound_file_to_edit_list(cp, tempfile,
3978 						 make_file_state(fd, hdr, chan, 0, FILE_BUFFER_SIZE),
3979 						 hdr, auto_delete, chan);
3980       ed->sound_location = ED_SOUND(cb);
3981 
3982       ripple_all(cp, 0, 0); /* copies marks/mixes */
3983       if (new_len > prev_len) reflect_sample_change_in_axis(cp);
3984 
3985       reflect_mix_change(ANY_MIX_ID);
3986       after_edit(cp);
3987 
3988       if (backup > 0)
3989 	{
3990 	  backup_edit_list(cp);
3991 	  if (backup > 1)
3992 	    backup_edit_list(cp);
3993 	}
3994     }
3995   else
3996     {
3997       Xen_error(NO_SUCH_FILE,
3998 		Xen_list_3(C_string_to_Xen_string("~A: ~A"),
3999 			   C_string_to_Xen_string(origin),
4000 			   C_string_to_Xen_string(snd_io_strerror())));
4001     }
4002   return(true);
4003 }
4004 
4005 
file_override_samples(mus_long_t num,const char * tempfile,chan_info * cp,int chan,file_delete_t auto_delete,const char * origin)4006 bool file_override_samples(mus_long_t num, const char *tempfile, chan_info *cp, int chan, file_delete_t auto_delete, const char *origin)
4007 {
4008   file_info *hdr;
4009   hdr = make_file_info(tempfile, FILE_READ_ONLY, FILE_NOT_SELECTED);
4010   if (hdr)
4011     {
4012       int fd;
4013       ed_list *e;
4014       if (num == -1) num = (hdr->samples / hdr->chans);
4015       if (!(prepare_edit_list(cp, AT_CURRENT_EDIT_POSITION, origin)))
4016 	{
4017 	  free_file_info(hdr);
4018 	  return(false);
4019 	}
4020       /* don't need to lock mixes here -- we're simply ignoring all previous edit state */
4021 
4022       fd = snd_open_read(tempfile);
4023       snd_file_open_descriptors(fd,
4024 				tempfile,
4025 				hdr->sample_type,
4026 				hdr->data_location,
4027 				hdr->chans,
4028 				hdr->type);
4029       during_open(fd, tempfile, SND_OVERRIDE_FILE);
4030       e = initial_ed_list(0, num - 1);
4031       if (origin)
4032 	e->origin = mus_strdup(origin);
4033       else e->origin = mus_strdup("file change samples");
4034       e->edit_type = CHANGE_EDIT;
4035       e->edpos = cp->edit_ctr - 1;
4036       e->samples = num;
4037       if (cp->edit_ctr > 0) e->cursor = cp->edits[cp->edit_ctr - 1]->cursor;
4038       cp->edits[cp->edit_ctr] = e;
4039 
4040       if (mus_sound_channel_maxamp_exists(tempfile, chan))
4041 	{
4042 	  mus_long_t pos;
4043 	  e->maxamp = mus_sound_channel_maxamp(tempfile, chan, &pos);
4044 	  e->maxamp_position = pos;
4045 	}
4046 
4047       FRAGMENT_SOUND(e, 0) = add_sound_file_to_edit_list(cp, tempfile,
4048 							 make_file_state(fd, hdr, chan, 0, FILE_BUFFER_SIZE),
4049 							 hdr, auto_delete, chan);
4050       e->sound_location = FRAGMENT_SOUND(e, 0);
4051 
4052       ripple_all(cp, 0, 0);
4053       reflect_sample_change_in_axis(cp);
4054       reflect_mix_change(ANY_MIX_ID);
4055       after_edit(cp);
4056     }
4057   else
4058     {
4059       Xen_error(NO_SUCH_FILE,
4060 		Xen_list_3(C_string_to_Xen_string("~A: ~A"),
4061 			   C_string_to_Xen_string(origin),
4062 			   C_string_to_Xen_string(snd_io_strerror())));
4063     }
4064   return(true);
4065 }
4066 
4067 
change_samples(mus_long_t beg,mus_long_t num,mus_float_t * vals,chan_info * cp,const char * origin,int edpos,mus_float_t mx)4068 bool change_samples(mus_long_t beg, mus_long_t num, mus_float_t *vals, chan_info *cp, const char *origin, int edpos, mus_float_t mx)
4069 {
4070   /* mx should be -1.0 except in the one case where vals is a block all of the same value */
4071   mus_long_t prev_len, new_len;
4072   ed_fragment *cb;
4073   ed_list *ed, *old_ed;
4074   int backup = 0;
4075 
4076   if (num <= 0) return(true);
4077   old_ed = cp->edits[edpos];
4078   prev_len = old_ed->samples;
4079   if (beg > prev_len)
4080     {
4081       if (!(extend_with_zeros(cp, prev_len, beg - prev_len, edpos, origin)))
4082 	return(false);
4083       edpos = cp->edit_ctr;
4084       prev_len = current_samples(cp);
4085       backup++;
4086     }
4087   new_len = beg + num;
4088   if (new_len < prev_len) new_len = prev_len;
4089   if (!(prepare_edit_list(cp, edpos, origin)))
4090     return(false);
4091   if (lock_affected_mixes(cp, edpos, beg, beg + num))
4092     {
4093       edpos = cp->edit_ctr;
4094       increment_edit_ctr(cp);
4095       backup++;
4096     }
4097 
4098   ed = change_samples_in_list(beg, num, edpos, cp, &cb, origin);
4099   ed->edit_type = CHANGE_EDIT;
4100   ed->samples = new_len;
4101   cp->edits[cp->edit_ctr] = ed;
4102   if (cp->edit_ctr > 0) ed->cursor = cp->edits[cp->edit_ctr - 1]->cursor;
4103   prepare_sound_list(cp);
4104   cp->sounds[cp->sound_ctr] = make_snd_data_buffer(vals, (int)num, cp->edit_ctr);
4105   ED_SOUND(cb) = cp->sound_ctr;
4106   ed->sound_location = ED_SOUND(cb);
4107 
4108   if ((old_ed->maxamp_position >= 0) ||
4109       ((beg == 0) && (num >= old_ed->samples)))  /* perhaps old max is irrelevant */
4110     {
4111       int pos = 0;
4112       if ((mx < 0.0) &&
4113 	  (num < MAXAMP_CHECK_SIZE))
4114 	{
4115 	  mus_float_t nmx;
4116 	  int i;
4117 	  nmx = fabs(vals[0]);
4118 	  for (i = 1; i < num; i++)
4119 	    {
4120 	      mus_float_t temp;
4121 	      temp = fabs(vals[i]);
4122 	      if (temp > nmx)
4123 		{
4124 		  nmx = temp;
4125 		  pos = i;
4126 		}
4127 	    }
4128 	  mx = nmx;
4129 	}
4130       if (mx >= 0.0)
4131 	{
4132 	  if ((mx > old_ed->maxamp) ||
4133 	      ((beg == 0) && (num >= old_ed->samples)))
4134 	    {
4135 	      ed->maxamp = mx;
4136 	      ed->maxamp_position = beg + pos;
4137 	    }
4138 	  else
4139 	    {
4140 	      if ((old_ed->maxamp_position < beg) ||
4141 		  (old_ed->maxamp_position > (beg + num)))
4142 		{
4143 		  ed->maxamp = old_ed->maxamp;
4144 		  ed->maxamp_position = old_ed->maxamp_position;
4145 		}
4146 	    }
4147 	}
4148     }
4149 
4150   ripple_all(cp, 0, 0);
4151   if (new_len > prev_len) reflect_sample_change_in_axis(cp);
4152   reflect_mix_change(ANY_MIX_ID);
4153   after_edit(cp);
4154 
4155   if (backup > 0)
4156     {
4157       backup_edit_list(cp);
4158       if (backup > 1)
4159 	backup_edit_list(cp);
4160     }
4161 
4162   return(true);
4163 }
4164 
4165 
4166 /* -------------------------------- ramp/scale -------------------------------- */
4167 
unrampable(chan_info * cp,mus_long_t beg,mus_long_t dur,int pos,bool is_xramp)4168 bool unrampable(chan_info *cp, mus_long_t beg, mus_long_t dur, int pos, bool is_xramp)
4169 {
4170   /* from enveloper (snd-sig.c) */
4171   ed_list *ed;
4172   int i;
4173   mus_long_t end;
4174   ed = cp->edits[pos];
4175   end = beg + dur - 1;
4176   for (i = 0; i < ed->size - 1; i++)
4177     {
4178       mus_long_t loc, next_loc;
4179       int typ;
4180       if (FRAGMENT_SOUND(ed, i) == EDIT_LIST_END_MARK) return(false);
4181       loc = FRAGMENT_GLOBAL_POSITION(ed, i);
4182       if (loc > end) return(false);
4183       typ = FRAGMENT_TYPE(ed, i);
4184       next_loc = FRAGMENT_GLOBAL_POSITION(ed, i + 1);             /* i.e. next loc = current fragment end point */
4185       /* fragment starts at loc, ends just before next_loc, is of type typ */
4186       if ((next_loc > beg) &&
4187 	  (((!is_xramp) &&
4188 	    (type_info[typ].add_ramp == -1)) ||
4189 	   ((is_xramp) &&
4190 	    ((type_info[typ].add_xramp == -1)))))
4191 	return(true);
4192     }
4193   return(false);
4194 }
4195 
4196 
sound_fragments_in_use(chan_info * cp,int pos)4197 bool sound_fragments_in_use(chan_info *cp, int pos)
4198 {
4199   /* (swap-channels): are there any non-simple/non-ramp edits? */
4200   int i;
4201   ed_list *ed;
4202   ed = cp->edits[pos];
4203   for (i = 0; i < ed->size - 1; i++)
4204     {
4205       int index;
4206       index = FRAGMENT_SOUND(ed, i);
4207       if (index == EDIT_LIST_END_MARK) return(false);
4208       if ((index != 0) &&
4209 	  (index != EDIT_LIST_ZERO_MARK))
4210 	return(true);
4211     }
4212   return(false);
4213 }
4214 
4215 
virtual_mix_ok(chan_info * cp,int edpos)4216 bool virtual_mix_ok(chan_info *cp, int edpos)
4217 {
4218   /* since a mix can be dragged anywhere, and we want to continue treating it as a virtual mix anywhere,
4219    *   we have to make sure that all edits in the current edit list are mix-able.
4220    */
4221   ed_list *ed;
4222   int i;
4223   ed = cp->edits[edpos];
4224   for (i = 0; i < ed->size - 1; i++)
4225     {
4226       if (FRAGMENT_SOUND(ed, i) == EDIT_LIST_END_MARK) return(true);
4227       if (!(is_mixable_op(FRAGMENT_TYPE(ed, i)))) return(false);
4228     }
4229   return(true);
4230 }
4231 
4232 
found_virtual_mix(chan_info * cp,int edpos)4233 static bool found_virtual_mix(chan_info *cp, int edpos)
4234 {
4235   ed_list *ed;
4236   int i;
4237   ed = cp->edits[edpos];
4238   for (i = 0; i < ed->size - 1; i++)
4239     {
4240       if (FRAGMENT_SOUND(ed, i) == EDIT_LIST_END_MARK) return(false);
4241       if (is_mix_op(FRAGMENT_TYPE(ed, i))) return(true);
4242     }
4243   return(false);
4244 }
4245 
4246 
found_unmixable_ramped_op(chan_info * cp,int edpos,mus_long_t beg,mus_long_t end,bool is_xramp)4247 static bool found_unmixable_ramped_op(chan_info *cp, int edpos, mus_long_t beg, mus_long_t end, bool is_xramp)
4248 {
4249   ed_list *ed;
4250   int i;
4251   ed = cp->edits[edpos];
4252   for (i = 0; i < ed->size - 1; i++)
4253     {
4254       mus_long_t loc, next_loc;
4255       if (FRAGMENT_SOUND(ed, i) == EDIT_LIST_END_MARK) return(false);
4256       loc = FRAGMENT_GLOBAL_POSITION(ed, i);
4257       if (loc > end) return(false);
4258       next_loc = FRAGMENT_GLOBAL_POSITION(ed, i + 1);         /* i.e. next loc = current fragment end point */
4259       /* this fragment (i) starts at loc, ends just before next_loc, is of type typ */
4260       if (next_loc > beg)
4261 	{
4262 	  int after_ramp_op;
4263 	  if (is_xramp)
4264 	    after_ramp_op = type_info[FRAGMENT_TYPE(ed, i)].add_xramp;
4265 	  else after_ramp_op = type_info[FRAGMENT_TYPE(ed, i)].add_ramp;
4266 	  if ((after_ramp_op == -1) ||        /* this should not happen since we check for it ahead of time */
4267 	      (!(is_mixable_op(after_ramp_op))))
4268 	    return(true);
4269 	}
4270     }
4271   return(false);
4272 }
4273 
4274 
section_is_zero(chan_info * cp,mus_long_t beg,mus_long_t dur,int pos)4275 static bool section_is_zero(chan_info *cp, mus_long_t beg, mus_long_t dur, int pos)
4276 {
4277   ed_list *ed;
4278   int i;
4279   mus_long_t end;
4280 
4281   ed = cp->edits[pos];
4282   end = beg + dur - 1;
4283   for (i = 0; i < ed->size - 1; i++)
4284     {
4285       mus_long_t loc, next_loc;
4286       int typ;
4287       if (FRAGMENT_SOUND(ed, i) == EDIT_LIST_END_MARK) return(true);
4288       loc = FRAGMENT_GLOBAL_POSITION(ed, i);
4289       if (loc > end) return(true);
4290       typ = FRAGMENT_TYPE(ed, i);
4291       next_loc = FRAGMENT_GLOBAL_POSITION(ed, i + 1);         /* i.e. next loc = current fragment end point */
4292       /* this fragment (i) starts at loc, ends just before next_loc, is of type typ */
4293       if ((next_loc > beg) &&
4294 	  (typ != ED_ZERO))
4295 	return(false);
4296     }
4297   return(true);
4298 }
4299 
4300 
copy_and_split_list(mus_long_t beg,mus_long_t num,ed_list * current_state)4301 static ed_list *copy_and_split_list(mus_long_t beg, mus_long_t num, ed_list *current_state)
4302 {
4303   mus_long_t end, next_pos;
4304   int cur_len, cur_i, new_i;
4305   ed_list *new_state;
4306   ed_fragment *new_f, *mid_f = NULL;
4307   if (num <= 0) return(NULL);
4308   cur_len = current_state->size;
4309   end = beg + num;
4310   new_state = make_ed_list(cur_len + 2); /* leave room for possible split */
4311   for (cur_i = 0, new_i = 0; cur_i < cur_len; cur_i++, new_i++)
4312     {
4313       ed_fragment *cur_f;
4314       cur_f = FRAGMENT(current_state, cur_i);
4315       new_f = FRAGMENT(new_state, new_i);
4316       if (ED_GLOBAL_POSITION(cur_f) >= end)
4317 	{
4318 	  /* after any split, copy this fragment */
4319 	  copy_ed_fragment(new_f, cur_f);
4320 	}
4321       else
4322 	{
4323 	  next_pos = FRAGMENT_GLOBAL_POSITION(current_state, (cur_i + 1));
4324 	  if (next_pos <= beg)
4325 	    {
4326 	      /* we're before any split, just copy */
4327 	      copy_ed_fragment(new_f, cur_f);
4328 	    }
4329 	  else
4330 	    {
4331 	      if ((ED_GLOBAL_POSITION(cur_f) >= beg) && (next_pos < end))
4332 		{
4333 		  /* entire segment is included */
4334 		  copy_ed_fragment(new_f, cur_f);
4335 		}
4336 	      else
4337 		{
4338 		  /* check for front and back splits, copy cur */
4339 		  copy_ed_fragment(new_f, cur_f);
4340 		  if (ED_GLOBAL_POSITION(cur_f) < beg)
4341 		    {
4342 		      ed_fragment *split_front_f, *split_back_f;
4343 
4344 		      /* split current at samp */
4345 		      split_front_f = new_f;
4346 		      new_i++;
4347 		      split_back_f = FRAGMENT(new_state, new_i);
4348 		      copy_ed_fragment(split_back_f, cur_f);
4349 		      new_f = split_back_f;
4350 		      ED_LOCAL_END(split_front_f) = ED_LOCAL_POSITION(split_front_f) + beg - ED_GLOBAL_POSITION(split_front_f) - 1;
4351 		      /* beg - global position = where in current fragment, offset that by its local offset, turn into end sample */
4352 		      ED_LOCAL_POSITION(split_back_f) = ED_LOCAL_END(split_front_f) + 1;
4353 		      ED_GLOBAL_POSITION(split_back_f) = beg;
4354 
4355 		      /* now fixup ramps affected by the split */
4356 		      if (ramp_op(ED_TYPE(cur_f)))
4357 			{
4358 			  new_before_ramp(split_front_f, cur_f);
4359 			  new_after_ramp(split_back_f, cur_f, beg);
4360 			  mid_f = split_back_f;
4361 			}
4362 		    }
4363 
4364 		  if (next_pos > end)
4365 		    {
4366 		      ed_fragment *split_front_f, *split_back_f;
4367 
4368 		      /* split current at samp */
4369 		      split_front_f = new_f;
4370 		      new_i++;
4371 		      split_back_f = FRAGMENT(new_state, new_i);
4372 		      copy_ed_fragment(split_back_f, cur_f);
4373 		      ED_LOCAL_END(split_front_f) = ED_LOCAL_POSITION(split_front_f) + end - ED_GLOBAL_POSITION(split_front_f) - 1;
4374 		      ED_LOCAL_POSITION(split_back_f) = ED_LOCAL_END(split_front_f) + 1;
4375 		      ED_GLOBAL_POSITION(split_back_f) = end;
4376 
4377 		      /* now fixup ramps affected by the split */
4378 		      if (ramp_op(ED_TYPE(cur_f)))
4379 			{
4380 			  if (ED_RAMPS(split_front_f))
4381 			    {
4382 			      mus_float_t *ramp_begs = NULL, *xramp_begs = NULL;
4383 			      int i, rmps, xrmps;
4384 
4385 			      rmps = ED_RAMP_LIST_SIZE(split_front_f);
4386 			      xrmps = ED_XRAMP_LIST_SIZE(split_front_f);
4387 			      if (rmps > 0) ramp_begs = (mus_float_t *)calloc(rmps, sizeof(mus_float_t));
4388 			      if (xrmps > 0) xramp_begs = (mus_float_t *)calloc(xrmps, sizeof(mus_float_t));
4389 
4390 			      for (i = 0; i < rmps; i++)
4391 				ramp_begs[i] = ED_RAMP_START(split_front_f, i);
4392 			      for (i = 0; i < xrmps; i++)
4393 				xramp_begs[i] = ED_XRAMP_START(split_front_f, i);
4394 
4395 			      new_before_ramp(split_front_f, cur_f);
4396 
4397 			      if (mid_f == split_front_f)
4398 				{
4399 				  for (i = 0; i < rmps; i++)
4400 				    ED_RAMP_START(split_front_f, i) = ramp_begs[i];
4401 				  for (i = 0; i < xrmps; i++)
4402 				    ED_XRAMP_START(split_front_f, i) = xramp_begs[i];
4403 				}
4404 
4405 			      if (ramp_begs) free(ramp_begs);
4406 			      if (xramp_begs) free(xramp_begs);
4407 			    }
4408 			  new_after_ramp(split_back_f, cur_f, end);
4409 			}
4410 		    }
4411 		}
4412 	    }
4413 	}
4414     }
4415   new_state->size = new_i;
4416   new_state->beg = beg;
4417   new_state->len = num;
4418   return(new_state);
4419 }
4420 
4421 
scale_channel_with_origin(chan_info * cp,mus_float_t scl,mus_long_t beg,mus_long_t num,int pos,bool in_as_one_edit,const char * origin)4422 bool scale_channel_with_origin(chan_info *cp, mus_float_t scl, mus_long_t beg, mus_long_t num, int pos, bool in_as_one_edit, const char *origin)
4423 {
4424   /* copy current ed-list and reset scalers */
4425   mus_long_t len = 0;
4426   int i, old_pos;
4427   ed_list *new_ed, *old_ed;
4428   bool backup = false;
4429 
4430   old_ed = cp->edits[pos];
4431   if ((beg < 0) ||
4432       (num <= 0) ||
4433       (beg >= old_ed->samples) ||
4434       ((scl == 1.0) && (pos == cp->edit_ctr)) ||
4435       (section_is_zero(cp, beg, num, pos)))
4436     return(false);
4437 
4438   old_pos = pos;
4439   len = old_ed->samples;
4440   if (!(prepare_edit_list(cp, pos, S_scale_channel)))
4441     return(false);
4442 
4443   if ((beg == 0) &&
4444       (num >= old_ed->samples))
4445     {
4446       if (scl == 0.0)
4447 	{
4448 	  new_ed = initial_ed_list(0, num - 1);
4449 	  FRAGMENT_SCALER(new_ed, 0) = 0.0;
4450 	  FRAGMENT_TYPE(new_ed, 0) = ED_ZERO;
4451 	  new_ed->maxamp = 0.0;
4452 	  new_ed->maxamp_position = 0;
4453 	}
4454       else
4455 	{
4456 	  num = len;
4457 	  new_ed = make_ed_list(old_ed->size);
4458 	  new_ed->beg = beg;
4459 	  new_ed->len = num;
4460 	  for (i = 0; i < new_ed->size; i++)
4461 	    {
4462 	      copy_ed_fragment(FRAGMENT(new_ed, i), FRAGMENT(old_ed, i));
4463 	      FRAGMENT_SCALER(new_ed, i) *= scl;
4464 	    }
4465 	  if (old_ed->maxamp_position != -1)
4466 	    {
4467 	      new_ed->maxamp = old_ed->maxamp * fabs(scl);
4468 	      new_ed->maxamp_position = old_ed->maxamp_position;
4469 	    }
4470 	}
4471       new_ed->samples = len;
4472       cp->edits[cp->edit_ctr] = new_ed;
4473       peak_env_scale_by(cp, scl, pos); /* this seems wasteful if this is an intermediate (in_as_one_edit etc) */
4474     }
4475   else
4476     {
4477       /* not sure how hard-nosed to be here -- we could probably get by if the scaled section completely encloses the mix(es) */
4478       if (lock_affected_mixes(cp, pos, beg, beg + num))
4479 	{
4480 	  pos = cp->edit_ctr;
4481 	  old_ed = cp->edits[pos];
4482 	  increment_edit_ctr(cp);
4483 	  backup = true;
4484 	}
4485 
4486       if (beg + num > len) num = len - beg;
4487       new_ed = copy_and_split_list(beg, num, old_ed);
4488       new_ed->samples = len;
4489       cp->edits[cp->edit_ctr] = new_ed;
4490       for (i = 0; i < new_ed->size; i++)
4491 	{
4492 	  if (FRAGMENT_GLOBAL_POSITION(new_ed, i) > (beg + num - 1)) break; /* not >= (1 sample selections) */
4493 	  if (FRAGMENT_GLOBAL_POSITION(new_ed, i) >= beg)
4494 	    {
4495 	      FRAGMENT_SCALER(new_ed, i) *= scl;
4496 	      if (scl == 0.0)
4497 		{
4498 		  FRAGMENT_TYPE(new_ed, i) = ED_ZERO;
4499 		  clear_ed_fragment(FRAGMENT(new_ed, i));
4500 		}
4501 	    }
4502 	}
4503       peak_env_scale_selection_by(cp, scl, beg, num, pos);
4504 
4505       if (old_ed->maxamp_position >= 0)
4506 	{
4507 	  if ((old_ed->maxamp_position < beg) ||
4508 	       (old_ed->maxamp_position > (beg + num)))
4509 	    {
4510 	      if (fabs(scl) <= 1.0)
4511 		{
4512 		  new_ed->maxamp = old_ed->maxamp;
4513 		  new_ed->maxamp_position = old_ed->maxamp_position;
4514 		}
4515 	      else
4516 		{
4517 		  /* perhaps this costs more than it saves */
4518 		  if (num < MAXAMP_CHECK_SIZE)
4519 		    {
4520 		      mus_float_t mx;
4521 		      int i, loc = 0;
4522 		      snd_fd *sf;
4523 		      sf = init_sample_read_any_with_bufsize(beg, cp, READ_FORWARD, old_pos, num + 1);
4524 		      sampler_set_safe(sf, num);
4525 		      mx = fabs(read_sample(sf));
4526 		      for (i = 1; i < num; i++)
4527 			{
4528 			  mus_float_t temp;
4529 			  temp = fabs(read_sample(sf));
4530 			  if (temp > mx)
4531 			    {
4532 			      mx = temp;
4533 			      loc = i;
4534 			    }
4535 			}
4536 		      free_snd_fd(sf);
4537 		      mx *= fabs(scl);
4538 		      if (mx > old_ed->maxamp)
4539 			{
4540 			  new_ed->maxamp = mx;
4541 			  new_ed->maxamp_position = beg + loc;
4542 			}
4543 		      else
4544 			{
4545 			  new_ed->maxamp = old_ed->maxamp;
4546 			  new_ed->maxamp_position = old_ed->maxamp_position;
4547 			}
4548 		    }
4549 		}
4550 	    }
4551 	  else
4552 	    {
4553 	      if (fabs(scl) >= 1.0)
4554 		{
4555 		  new_ed->maxamp = old_ed->maxamp * fabs(scl);
4556 		  new_ed->maxamp_position = old_ed->maxamp_position;
4557 		}
4558 	    }
4559 	}
4560     }
4561   new_ed->cursor = old_ed->cursor;
4562   new_ed->edit_type = SCALED_EDIT;
4563   new_ed->sound_location = 0;
4564   if (origin)
4565     new_ed->origin = mus_strdup(origin);
4566   else
4567     {
4568       if (num == len)
4569 #if HAVE_FORTH
4570 	new_ed->origin = mus_format("%.3f %" print_mus_long PROC_SEP PROC_FALSE " %s", scl, beg, S_scale_channel);
4571 #else
4572 	new_ed->origin = mus_format("%s" PROC_OPEN "%.3f" PROC_SEP "%" print_mus_long PROC_SEP PROC_FALSE, to_proc_name(S_scale_channel), scl, beg);
4573 #endif
4574       else
4575 	{
4576 #if HAVE_FORTH
4577 	  new_ed->origin = mus_format("%.3f %" print_mus_long PROC_SEP "%" print_mus_long " %s", scl, beg, num, S_scale_channel);
4578 #else
4579 	  new_ed->origin = mus_format("%s" PROC_OPEN "%.3f" PROC_SEP "%" print_mus_long PROC_SEP "%" print_mus_long, to_proc_name(S_scale_channel), scl, beg, num);
4580 #endif
4581 	}
4582     }
4583   new_ed->edpos = pos;
4584   new_ed->selection_beg = old_ed->selection_beg;
4585   new_ed->selection_end = old_ed->selection_end;
4586 
4587   ripple_marks(cp, 0, 0);
4588   ripple_mixes_with_scale(cp, beg, num, scl);
4589   check_for_first_edit(cp);
4590 
4591   if (!in_as_one_edit)
4592     {
4593       update_graph(cp);
4594       reflect_mix_change(ANY_MIX_ID);
4595       after_edit(cp);  /* "as_one_edit" here is an envelope, so it's not a "real" edit -- after_edit called explicitly in snd-sig.c in this case */
4596     }
4597 
4598   if (backup)
4599     backup_edit_list(cp);
4600 
4601   return(true);
4602 }
4603 
4604 
scale_channel(chan_info * cp,mus_float_t scl,mus_long_t beg,mus_long_t num,int pos,bool in_as_one_edit)4605 bool scale_channel(chan_info *cp, mus_float_t scl, mus_long_t beg, mus_long_t num, int pos, bool in_as_one_edit)
4606 {
4607   return(scale_channel_with_origin(cp, scl, beg, num, pos, in_as_one_edit, NULL));
4608 }
4609 
4610 
add_ramp_to_fragment(ed_list * new_ed,int i,double start,double incr,mus_float_t scaler,mus_float_t offset,bool is_xramp)4611 static void add_ramp_to_fragment(ed_list *new_ed, int i, double start, double incr, mus_float_t scaler, mus_float_t offset, bool is_xramp)
4612 {
4613   ed_fragment *ed;
4614   int rmps = 0, xrmps = 0, loc, typ;
4615 
4616   ed = FRAGMENT(new_ed, i);
4617   if (ED_TYPE(ed) == ED_ZERO) return;
4618 
4619   if ((!is_xramp) && (start == 0.0) && (incr == 0.0))
4620     {
4621       ED_TYPE(ed) = ED_ZERO;
4622       clear_ed_fragment(ed);
4623       return;
4624     }
4625 
4626   if (ED_RAMPS(ed))
4627     {
4628       rmps = ED_RAMP_LIST_SIZE(ed);
4629       xrmps = ED_XRAMP_LIST_SIZE(ed);
4630     }
4631   if (is_xramp)
4632     xrmps++;
4633   else rmps++;
4634 
4635   ensure_ed_ramps(ed, rmps, xrmps);
4636   typ = ED_TYPE(ed);
4637 
4638   if (is_xramp)
4639     {
4640       loc = xrmps - 1;
4641       ED_XRAMP_START(ed, loc) = start;
4642       ED_XRAMP_INCR(ed, loc) = incr;
4643       ED_XRAMP_SCALER(ed, loc) = scaler;
4644       ED_XRAMP_OFFSET(ed, loc) = offset;
4645       ED_TYPE(ed) = type_info[typ].add_xramp;
4646     }
4647   else
4648     {
4649       loc = rmps - 1;
4650       ED_RAMP_START(ed, loc) = start;
4651       ED_RAMP_INCR(ed, loc) = incr;
4652       ED_TYPE(ed) = type_info[typ].add_ramp;
4653     }
4654 }
4655 
4656 
all_ramp_channel(chan_info * cp,double start,double incr,double scaler,double offset,mus_long_t beg,mus_long_t num,int pos,bool in_as_one_edit,const char * origin,bool is_xramp,mus_any * e,int xramp_seg_loc)4657 static bool all_ramp_channel(chan_info *cp, double start, double incr, double scaler, double offset,
4658 			     mus_long_t beg, mus_long_t num, int pos, bool in_as_one_edit, const char *origin,
4659 			     bool is_xramp, mus_any *e, int xramp_seg_loc)
4660 {
4661   mus_long_t len = 0;
4662   int i, old_pos;
4663   ed_list *new_ed, *old_ed;
4664   bool backup = false;
4665   double rstart;
4666   /*
4667   fprintf(stderr,"ramp: %f %f %f %f %" print_mus_long " %" print_mus_long "\n", start, incr, scaler, offset, beg, num);
4668   */
4669 
4670   old_ed = cp->edits[pos];
4671   if ((beg < 0) ||
4672       (num <= 0) ||
4673       (beg >= old_ed->samples) ||
4674       (section_is_zero(cp, beg, num, pos)))
4675     return(false);  /* was true, but this is a no-op */
4676 
4677   if ((!is_xramp) && ((incr == 0.0) || (num == 1)))                 /* in xramp case, we're ramping a power, not a scaler */
4678     return(scale_channel(cp, start, beg, num, pos, in_as_one_edit));
4679 
4680   len = old_ed->samples;
4681   old_pos = pos;
4682 
4683   if (!(prepare_edit_list(cp, pos, origin)))
4684     return(false);
4685 
4686   if (found_virtual_mix(cp, pos))
4687     {
4688       mus_long_t lock_beg, lock_end;
4689       if (found_unmixable_ramped_op(cp, pos, beg, beg + num, is_xramp))
4690 	{
4691 	  lock_beg = 0;
4692 	  lock_end = len - 1;
4693 	}
4694       else
4695 	{
4696 	  lock_beg = beg;
4697 	  lock_end = beg + num;
4698 	}
4699       if (lock_affected_mixes(cp, pos, lock_beg, lock_end))
4700 	{
4701 	  pos = cp->edit_ctr;
4702 	  old_ed = cp->edits[pos];
4703 	  increment_edit_ctr(cp);
4704 	  backup = true;
4705 	}
4706     }
4707 
4708   rstart = start;
4709   if ((beg == 0) &&
4710       (num >= old_ed->samples))
4711     {
4712       /* one ramp over entire fragment list -- no splits will occur here */
4713       num = len;
4714       new_ed = make_ed_list(old_ed->size);
4715       new_ed->beg = beg;
4716       new_ed->len = num;
4717       cp->edits[cp->edit_ctr] = new_ed;
4718       for (i = 0; i < new_ed->size; i++)
4719 	copy_ed_fragment(FRAGMENT(new_ed, i), FRAGMENT(old_ed, i));
4720       for (i = 0; i < new_ed->size - 1; i++) /* -1 here to leave end mark alone */
4721 	{
4722 	  add_ramp_to_fragment(new_ed, i, start, incr, scaler, offset, is_xramp);
4723 	  if (!is_xramp)
4724 	    start += (incr * FRAGMENT_LENGTH(new_ed, i));
4725 	  else start *= exp(log(incr) * FRAGMENT_LENGTH(new_ed, i));
4726 	}
4727     }
4728   else
4729     {
4730       if (beg + num > len) num = len - beg;
4731       new_ed = copy_and_split_list(beg, num, old_ed);
4732       cp->edits[cp->edit_ctr] = new_ed;
4733       for (i = 0; i < new_ed->size - 1; i++)
4734 	{
4735 	  if (FRAGMENT_GLOBAL_POSITION(new_ed, i) > (beg + num - 1)) break; /* not >= (1 sample selections) */
4736 	  if (FRAGMENT_GLOBAL_POSITION(new_ed, i) >= beg)
4737 	    {
4738 	      add_ramp_to_fragment(new_ed, i, start, incr, scaler, offset, is_xramp);
4739 	      if (!is_xramp)
4740 		start += (incr * FRAGMENT_LENGTH(new_ed, i));
4741 	      else start *= exp(log(incr) * FRAGMENT_LENGTH(new_ed, i));
4742 	    }
4743 	}
4744       if ((old_ed->maxamp_position >= 0) ||
4745 	  ((beg == 0) && (num >= old_ed->samples)))
4746 	{
4747 	  if ((old_ed->maxamp_position >= 0) &&
4748 	      ((old_ed->maxamp_position < beg) ||
4749 	       (old_ed->maxamp_position > (beg + num))) &&
4750 	      (fabs(rstart) <= 1.0) &&
4751 	      (((!is_xramp) &&
4752 		(fabs(rstart + incr * num) <= 1.0)) ||
4753 	       ((is_xramp) &&
4754 		(fabs(rstart * exp(log(incr) * num)) <= 1.0))))
4755 	    {
4756 	      /* we have maxamp data for the previous edit and
4757 	       *   the ramp does not hit the current maxamp and it stays within -1.0 to 1.0,
4758 	       *   so it can't affect the maxamp
4759 	       */
4760 	      new_ed->maxamp = old_ed->maxamp;
4761 	      new_ed->maxamp_position = old_ed->maxamp_position;
4762 	    }
4763 	  else
4764 	    {
4765 	      /* if ramped portion has a max > old max, we can use it in any case,
4766 	       *   but we need to do the ramp by hand here, I think.
4767 	       */
4768 	      if ((num < MAXAMP_CHECK_SIZE) &&
4769 		  (!is_xramp))
4770 		{
4771 		  mus_float_t mx, x;
4772 		  int i, loc = 0;
4773 		  snd_fd *sf;
4774 		  x = rstart;
4775 		  sf = init_sample_read_any_with_bufsize(beg, cp, READ_FORWARD, old_pos, num + 1);
4776 		  sampler_set_safe(sf, num);
4777 		  mx = fabs(x * read_sample(sf));
4778 		  for (i = 1; i < num; i++)
4779 		    {
4780 		      mus_float_t temp;
4781 		      x += incr;
4782 		      temp = fabs(x * read_sample(sf));
4783 		      if (temp > mx)
4784 			{
4785 			  mx = temp;
4786 			  loc = i;
4787 			}
4788 		    }
4789 		  free_snd_fd(sf);
4790 		  if ((mx > old_ed->maxamp) ||
4791 		      ((beg == 0) && (num >= old_ed->samples)))
4792 		    {
4793 		      new_ed->maxamp = mx;
4794 		      new_ed->maxamp_position = beg + loc;
4795 		    }
4796 		  else
4797 		    {
4798 		      if ((old_ed->maxamp_position < beg) ||
4799 			  (old_ed->maxamp_position > (beg + num)))
4800 			{
4801 			  new_ed->maxamp = old_ed->maxamp;
4802 			  new_ed->maxamp_position = old_ed->maxamp_position;
4803 			}
4804 		    }
4805 		}
4806 	    }
4807 	}
4808     }
4809   new_ed->samples = len;
4810   new_ed->cursor = old_ed->cursor;
4811   new_ed->edit_type = RAMP_EDIT;
4812   new_ed->sound_location = 0;
4813   if (!is_xramp)
4814     {
4815       mus_float_t rmp0, rmp1;
4816       rmp0 = rstart;
4817       rmp1 = rstart + incr * (num - 1); /* want end point */
4818 #if HAVE_FORTH
4819       if (num == len)
4820 	new_ed->origin = mus_format("%.3f %.3f %" print_mus_long PROC_SEP PROC_FALSE " %s", rmp0, rmp1, beg, origin);
4821       else
4822 	new_ed->origin = mus_format("%.3f %.3f %" print_mus_long PROC_SEP "%" print_mus_long " %s", rmp0, rmp1, beg, num, origin);
4823 #else
4824       if (num == len)
4825 	new_ed->origin = mus_format("%s" PROC_OPEN "%.3f" PROC_SEP "%.3f" PROC_SEP "%" print_mus_long PROC_SEP PROC_FALSE, to_proc_name(origin), rmp0, rmp1, beg);
4826       else
4827 	new_ed->origin = mus_format("%s" PROC_OPEN "%.3f" PROC_SEP "%.3f" PROC_SEP "%" print_mus_long PROC_SEP "%" print_mus_long, to_proc_name(origin), rmp0, rmp1, beg, num);
4828 #endif
4829     }
4830   else
4831     {
4832       mus_float_t *data;
4833       data = mus_data(e);
4834 #if HAVE_FORTH
4835       if (num == len)
4836 	new_ed->origin = mus_format("%.3f %.3f %.3f %" print_mus_long PROC_SEP PROC_FALSE " %s",
4837 				    data[xramp_seg_loc * 2 + 1], data[xramp_seg_loc * 2 + 3], mus_increment(e), beg, origin);
4838       else
4839 	new_ed->origin = mus_format("%.3f %.3f %.3f %" print_mus_long PROC_SEP "%" print_mus_long " %s",
4840 				    data[xramp_seg_loc * 2 + 1], data[xramp_seg_loc * 2 + 3], mus_increment(e), beg, num, origin);
4841 #else
4842       if (num == len)
4843 	new_ed->origin = mus_format("%s" PROC_OPEN "%.3f" PROC_SEP "%.3f" PROC_SEP "%.3f" PROC_SEP "%" print_mus_long PROC_SEP PROC_FALSE,
4844 				    to_proc_name(origin), data[xramp_seg_loc * 2 + 1], data[xramp_seg_loc * 2 + 3], mus_increment(e), beg);
4845       else
4846 	new_ed->origin = mus_format("%s" PROC_OPEN "%.3f" PROC_SEP "%.3f" PROC_SEP "%.3f" PROC_SEP "%" print_mus_long PROC_SEP "%" print_mus_long,
4847 				    to_proc_name(origin), data[xramp_seg_loc * 2 + 1], data[xramp_seg_loc * 2 + 3], mus_increment(e), beg, num);
4848 #endif
4849     }
4850   new_ed->edpos = pos;
4851   new_ed->selection_beg = old_ed->selection_beg;
4852   new_ed->selection_end = old_ed->selection_end;
4853 
4854   ripple_all(cp, 0, 0); /* 0,0 -> copy marks */
4855   reflect_mix_change(ANY_MIX_ID);
4856   after_edit(cp);
4857 
4858   if (backup)
4859     backup_edit_list(cp);
4860 
4861   return(true);
4862 }
4863 
4864 
ramp_channel(chan_info * cp,double start,double incr,mus_long_t beg,mus_long_t num,int pos,bool in_as_one_edit)4865 bool ramp_channel(chan_info *cp, double start, double incr, mus_long_t beg, mus_long_t num, int pos, bool in_as_one_edit)
4866 {
4867   return(all_ramp_channel(cp, start, incr, 0.0, 0.0, beg, num, pos, in_as_one_edit, S_ramp_channel, false, NULL, 0));
4868 }
4869 
4870 
xramp_channel(chan_info * cp,double start,double incr,double scaler,double offset,mus_long_t beg,mus_long_t num,int pos,bool in_as_one_edit,mus_any * e,int xramp_seg_loc)4871 bool xramp_channel(chan_info *cp, double start, double incr, double scaler, double offset,
4872 		   mus_long_t beg, mus_long_t num, int pos, bool in_as_one_edit, mus_any *e, int xramp_seg_loc)
4873 {
4874   return(all_ramp_channel(cp, start, incr, scaler, offset, beg, num, pos, in_as_one_edit, S_xramp_channel, true, e, xramp_seg_loc));
4875 }
4876 
4877 
4878 
4879 
4880 
4881 /* -------------------------------- samplers -------------------------------- */
4882 
free_reader_mixes(reader_mixes * md)4883 static reader_mixes *free_reader_mixes(reader_mixes *md)
4884 {
4885   if (md)
4886     {
4887       if ((md->size > 0) && (md->sfs))
4888 	{
4889 	  int i;
4890 	  for (i = 0; i < md->size; i++)
4891 	    if (md->sfs[i])
4892 	      {
4893 		if (md->sfs[i]->current_state)
4894 		  md->sfs[i]->current_state = free_ed_list(md->sfs[i]->current_state, md->sfs[i]->cp);  /* md->cp is ignored in this case -- MIX_EDIT */
4895 		md->sfs[i] = free_snd_fd(md->sfs[i]);
4896 	      }
4897 	  free(md->sfs);
4898 	}
4899       free(md);
4900     }
4901   return(NULL);
4902 }
4903 
4904 
free_snd_fd_almost(snd_fd * sf)4905 snd_fd *free_snd_fd_almost(snd_fd *sf)
4906 {
4907   if ((sf) && (!(sf->freed)))
4908     {
4909       if (sf->ramps)
4910 	{
4911 	  if (READER_INCRS(sf)) free(READER_INCRS(sf));
4912 	  if (READER_VALS(sf)) free(READER_VALS(sf));
4913 	  if (READER_XINCRS(sf)) free(READER_XINCRS(sf));
4914 	  if (READER_XVALS(sf)) free(READER_XVALS(sf));
4915 
4916 	  free(sf->ramps);
4917 	  sf->ramps = NULL;
4918 	}
4919 
4920       if (sf->mixes)
4921 	sf->mixes = (void *)free_reader_mixes((reader_mixes *)(sf->mixes));
4922 
4923       reader_out_of_data(sf);
4924       {
4925 	snd_data *sd;
4926 	sd = sf->current_sound;
4927 	if ((sd) &&
4928 	    ((sd->type == SND_DATA_BUFFER) || (sd->type == SND_DATA_FILE)))
4929 	  {
4930 	    sd->inuse = false;
4931 	    if ((sd->copy) || (sd->free_me))
4932 	      free_snd_data(sd);
4933 	  }
4934       }
4935       sf->current_sound = NULL;
4936       if (sf->current_state)
4937 	{
4938 	  if (sf->type == MIX_READER)
4939 	    sf->current_state = free_ed_list(sf->current_state, sf->cp);
4940 	  sf->current_state = NULL;
4941 	}
4942       sf->cp = NULL;
4943       sf->local_sp = NULL;
4944       sf->cb = NULL;
4945       sf->region = INVALID_REGION;
4946       sf->edit_ctr = -1;
4947       sf->freed = true;
4948     }
4949   return(NULL);
4950 }
4951 
4952 
free_snd_fd(snd_fd * sf)4953 snd_fd *free_snd_fd(snd_fd *sf)
4954 {
4955   if ((sf) && (!(sf->freed)))
4956     {
4957       free_snd_fd_almost(sf);
4958       free(sf);
4959     }
4960   return(NULL);
4961 }
4962 
4963 
current_location(snd_fd * sf)4964 mus_long_t current_location(snd_fd *sf)
4965 {
4966   /* only used by moving cursor code in snd-dac.c [and sampler-position] */
4967   if (sf->current_sound)
4968     return(READER_GLOBAL_POSITION(sf) - READER_LOCAL_POSITION(sf) + io_beg(sf->current_sound->io) + sf->loc);
4969   return(READER_GLOBAL_POSITION(sf) - READER_LOCAL_POSITION(sf) + sf->loc);
4970 }
4971 
4972 
sampler_set_safe(snd_fd * sf,mus_long_t dur)4973 void sampler_set_safe(snd_fd *sf, mus_long_t dur)
4974 {
4975   /* tricky because we currently assume the reader protects against reading off the end by returning 0.0,
4976    *  perhaps pass in dur (= number of samples we will be reading), and if last-loc+1>=dur, we're safe?
4977    *  dur here has to match the number of samples we will read!
4978    */
4979 
4980   /* fprintf(stderr, "%" print_mus_long " %" print_mus_long " %" print_mus_long ": %" print_mus_long "\n", sf->first, sf->loc, sf->last, dur); */
4981 
4982   if ((sf->last - sf->loc + 1) >= dur) /* two kinds of counter here: last is sample number, dur is how many samples */
4983     {
4984       if (sf->runf == next_sample_value_unscaled)
4985 	sf->runf = next_sample_value_unscaled_and_unchecked;
4986       else
4987 	{
4988 	  if (sf->runf == next_sample_value)
4989 	    sf->runf = next_sample_value_unchecked;
4990 	  else
4991 	    {
4992 	      if (sf->runf == next_ramp1)
4993 		sf->runf = next_ramp1_unchecked;
4994 	    }
4995 	}
4996     }
4997   else
4998     {
4999       if (sf->loc - sf->first + 1 >= dur)
5000 	{
5001 	  if (sf->runf == previous_sample_value_unscaled)
5002 	    sf->runf = previous_sample_value_unscaled_and_unchecked;
5003 	  else
5004 	    {
5005 	      if (sf->runf == previous_sample_value)
5006 		sf->runf = previous_sample_value_unchecked;
5007 	    }
5008 	}
5009     }
5010 }
5011 
5012 
init_sample_read_any_with_bufsize(mus_long_t samp,chan_info * cp,read_direction_t direction,int edit_position,int bufsize)5013 snd_fd *init_sample_read_any_with_bufsize(mus_long_t samp, chan_info *cp, read_direction_t direction, int edit_position, int bufsize)
5014 {
5015   snd_fd *sf;
5016   snd_info *sp;
5017   ed_list *ed;
5018   int len, i;
5019   mus_long_t curlen;
5020   snd_data *first_snd = NULL;
5021   /* bufsize might be ignored here except in the copied buffer case -- we use the pre-exisiting buffer? */
5022 
5023   if (cp->active < CHANNEL_HAS_EDIT_LIST) return(NULL);
5024   if ((edit_position < 0) || (edit_position >= cp->edit_size)) return(NULL); /* was ">" not ">=": 6-Jan-05 */
5025 
5026   ed = cp->edits[edit_position];
5027   if (!ed) return(NULL);
5028 
5029   sp = cp->sound;
5030   if (sp->inuse == SOUND_IDLE) return(NULL);
5031 
5032   if ((sp->need_update) &&
5033       (!(sp->writing)))
5034     {
5035       if (mus_file_probe(sp->filename) == 0)
5036 	{
5037 	  snd_warning("%s no longer exists!", sp->short_filename);
5038 	  return(NULL);
5039 	}
5040       else
5041 	{
5042 	  time_t write_date;
5043 	  write_date = file_write_date(sp->filename);
5044 	  if (sp->update_warning_write_date != write_date)
5045 	    {
5046 	      snd_warning("%s has changed since we last read it!", sp->short_filename);
5047 	      sp->update_warning_write_date = write_date;
5048 	      /* without this write-date check, there are cases where this can get into a loop sending warnings */
5049 	    }
5050 	}
5051     }
5052 
5053   curlen = cp->edits[edit_position]->samples;
5054 
5055   /* snd_fd allocated only here */
5056   sf = (snd_fd *)calloc(1, sizeof(snd_fd)); /* only creation point (... oops -- see below...)*/
5057 
5058   sf->freed = false;
5059   sf->region = INVALID_REGION;
5060   sf->type = SAMPLER;
5061   sf->initial_samp = samp;
5062   sf->cp = cp;
5063   sf->fscaler = 1.0;
5064   sf->direction = direction;
5065   sf->current_state = ed;
5066   sf->edit_ctr = edit_position;
5067 
5068   if ((curlen <= 0) ||    /* no samples, not ed->len (delete->len = #deleted samps) */
5069       (samp < 0) ||       /* this should never happen */
5070       ((samp >= curlen) && (direction == READ_FORWARD)))
5071     return(cancel_reader(sf));
5072 
5073   if (samp >= curlen) samp = curlen - 1;
5074   len = ed->size;
5075 
5076   for (i = 0; i < len; i++)
5077     {
5078       ed_fragment *cb;
5079       cb = FRAGMENT(ed, i);
5080       if ((ED_GLOBAL_POSITION(cb) > samp) ||
5081 	  (ED_SOUND(cb) == EDIT_LIST_END_MARK))             /* i.e. we went one too far */
5082 	{
5083 	  mus_long_t ind0, ind1, indx;
5084 	  sf->cb = FRAGMENT(ed, i - 1);                     /* so back up one */
5085 	  sf->cbi = i - 1;
5086 	  sf->frag_pos = samp - READER_GLOBAL_POSITION(sf);
5087 	  ind0 = READER_LOCAL_POSITION(sf);                   /* cb->beg */
5088 	  indx = ind0 + sf->frag_pos;
5089 	  ind1 = READER_LOCAL_END(sf);                        /* cb->end */
5090 	  sf->fscaler = READER_SCALER(sf);
5091 	  if (zero_op(READER_TYPE(sf)))
5092 	    {
5093 	      sf->current_sound = NULL;
5094 	      sf->loc = indx;
5095 	      sf->first = ind0;
5096 	      sf->last = ind1;
5097 	      sf->data = NULL;
5098 	      choose_accessor(sf);
5099 	      return(sf);
5100 	    }
5101 	  first_snd = sf->cp->sounds[READER_SOUND(sf)];
5102 	  if (!first_snd)
5103 	    return(cancel_reader(sf));
5104 	  if (first_snd->type == SND_DATA_FILE)
5105 	    {
5106 	      /* since arbitrarily many work procs can be running in parallel, reading the same
5107 	       * data (edit tree sound file entries), we can't share the clm-style IO buffers since these contain
5108 	       * a local notion of current position which is not accessed on every sample by the
5109 	       * samplers (they trust their snd_fd indices); we wouldn't want to be
5110 	       * constantly jumping around and re-reading data buffers (in the worst case
5111 	       * many times per sample) anyway, so we copy the IO buffer, allocate a relatively
5112 	       * small(?? -- is this obsolete) data buffer, and then free all the copied snd_data stuff as soon as
5113 	       * the current reader is done.
5114 	       *
5115 	       * but if all the data is in the current buffer, it won't be moving?
5116 	       */
5117 	      if ((first_snd->inuse) ||
5118 		  (bufsize > FILE_BUFFER_SIZE))
5119 		{
5120 		  first_snd = copy_snd_data(first_snd, samp, bufsize);
5121 		  if (!first_snd)
5122 		    return(cancel_reader(sf));
5123 		}
5124 	      first_snd->inuse = true;
5125 	      sf->current_sound = first_snd;
5126 	      sf->data = first_snd->buffered_data;
5127 	      if (direction == READ_FORWARD)
5128 		file_buffers_forward(ind0, ind1, indx, sf, first_snd);
5129 	      else file_buffers_back(ind0, ind1, indx, sf, first_snd);
5130 	    }
5131 	  else
5132 	    {
5133 	      sf->current_sound = NULL;
5134 	      sf->data = first_snd->buffered_data;
5135 	      sf->first = ind0;
5136 	      sf->last = ind1;
5137 	      sf->loc = indx;
5138 	    }
5139 	  choose_accessor(sf);
5140 	  return(sf);
5141 	}
5142     }
5143   if (sf) free(sf);
5144   return(NULL);
5145 }
5146 
5147 
init_sample_read_any(mus_long_t samp,chan_info * cp,read_direction_t direction,int edit_position)5148 snd_fd *init_sample_read_any(mus_long_t samp, chan_info *cp, read_direction_t direction, int edit_position)
5149 {
5150   return(init_sample_read_any_with_bufsize(samp, cp, direction, edit_position, FILE_BUFFER_SIZE));
5151 }
5152 
5153 
init_sample_read(mus_long_t samp,chan_info * cp,read_direction_t direction)5154 snd_fd *init_sample_read(mus_long_t samp, chan_info *cp, read_direction_t direction)
5155 {
5156   return(init_sample_read_any_with_bufsize(samp, cp, direction, cp->edit_ctr, FILE_BUFFER_SIZE));
5157 }
5158 
5159 
chn_sample(mus_long_t samp,chan_info * cp,int pos)5160 mus_float_t chn_sample(mus_long_t samp, chan_info *cp, int pos)
5161 {
5162   snd_fd *sf;
5163   mus_float_t val = 0.0;
5164 
5165   /* pos is assumed to be right here, not AT_CURRENT_EDIT_POSITION for example */
5166   if ((cp->active < CHANNEL_HAS_EDIT_LIST) ||
5167       (samp < 0) ||
5168       (pos < 0) ||
5169       (pos >= cp->edit_size) ||
5170       (samp >= cp->edits[pos]->samples))
5171     return(0.0);
5172 
5173   /* try the quick case */
5174   if (pos == 0)
5175     {
5176       snd_data *sd;
5177       sd = cp->sounds[0];
5178       if ((sd) && (sd->io) && (io_beg(sd->io) <= samp) && (io_end(sd->io) >= samp))
5179 	return(sd->buffered_data[samp - io_beg(sd->io)]);
5180     }
5181 
5182   /* do it the hard way */
5183   sf = init_sample_read_any_with_bufsize(samp, cp, READ_FORWARD, pos, 2);
5184   if (sf)
5185     {
5186       val = read_sample(sf);
5187       free_snd_fd(sf);
5188     }
5189   return(val);
5190 }
5191 
5192 
previous_sound_1(snd_fd * sf)5193 static void previous_sound_1(snd_fd *sf)
5194 {
5195   mus_long_t ind0, ind1;
5196   bool at_start;
5197   if ((sf->cp) &&
5198       (sf->cp->active < CHANNEL_HAS_EDIT_LIST))
5199     {
5200       reader_out_of_data(sf);
5201       return;
5202     }
5203   at_start = ((!sf->cb) ||
5204 	      (!sf->current_sound) ||
5205 	      (READER_LOCAL_POSITION(sf) >= io_beg(sf->current_sound->io)));
5206   if (at_start)
5207     {
5208       snd_data *prev_snd;
5209       if (sf->current_sound)
5210 	{
5211 	  prev_snd = sf->current_sound;
5212 	  prev_snd->inuse = false;
5213 	  sf->current_sound = NULL;
5214 	  if (prev_snd->copy) free_snd_data(prev_snd);
5215 	}
5216       if (sf->cbi == 0)
5217 	{
5218 	  reader_out_of_data(sf);
5219 	  return;
5220 	}
5221       sf->cbi--;
5222       /* now start in the final portion of this block (if a file) */
5223       sf->cb = FRAGMENT((sf->current_state), sf->cbi);
5224       ind0 = READER_LOCAL_POSITION(sf);
5225       ind1 = READER_LOCAL_END(sf);
5226       sf->fscaler = READER_SCALER(sf);
5227       if (zero_op(READER_TYPE(sf)))
5228 	{
5229 	  sf->current_sound = NULL;
5230 	  sf->loc = ind1;
5231 	  sf->first = ind0;
5232 	  sf->last = ind1;
5233 	  sf->data = NULL;
5234 	}
5235       else
5236 	{
5237 	  prev_snd = sf->cp->sounds[READER_SOUND(sf)];
5238 	  if (prev_snd->type == SND_DATA_FILE)
5239 	    {
5240 	      if (prev_snd->inuse)
5241 		{
5242 		  prev_snd = copy_snd_data(prev_snd, ind0, FILE_BUFFER_SIZE);
5243 		  if (!prev_snd)
5244 		    {
5245 		      /* too many files open or something of that sort */
5246 		      reader_out_of_data(sf);
5247 		      return;
5248 		    }
5249 		}
5250 	      sf->data = prev_snd->buffered_data;
5251 	      prev_snd->inuse = true;
5252 	      sf->current_sound = prev_snd;
5253 	      file_buffers_back(ind0, ind1, ind1, sf, prev_snd);
5254 	    }
5255 	  else
5256 	    {
5257 	      sf->data = prev_snd->buffered_data;
5258 	      sf->loc = ind1;
5259 	      sf->first = ind0;
5260 	      sf->last = ind1;
5261 	    }
5262 	}
5263       sf->frag_pos = ind1 - ind0;
5264       choose_accessor(sf);
5265     }
5266   else
5267     {
5268       mus_long_t indx;
5269       /* back up in current file */
5270       ind0 = READER_LOCAL_POSITION(sf);
5271       ind1 = READER_LOCAL_END(sf);
5272       indx = io_beg(sf->current_sound->io) - 1;
5273       file_buffers_back(ind0, ind1, indx, sf, sf->current_sound);
5274     }
5275 }
5276 
5277 
previous_sound(snd_fd * sf)5278 static mus_float_t previous_sound(snd_fd *sf)
5279 {
5280   previous_sound_1(sf);
5281   return(read_sample(sf));
5282 }
5283 
5284 
next_sound_1(snd_fd * sf)5285 static void next_sound_1(snd_fd *sf)
5286 {
5287   mus_long_t ind0, ind1;
5288   bool at_end = false;
5289 
5290   if ((sf->cp) &&
5291       (sf->cp->active < CHANNEL_HAS_EDIT_LIST))
5292     {
5293       reader_out_of_data(sf);
5294       return;
5295     }
5296   at_end = ((!sf->cb) ||
5297 	    (!sf->current_sound) ||
5298 	    (READER_LOCAL_END(sf) <= io_end(sf->current_sound->io)));
5299 
5300   if (at_end)
5301     {
5302       snd_data *nxt_snd;
5303       if (sf->current_sound)
5304 	{
5305 	  nxt_snd = sf->current_sound;
5306 	  nxt_snd->inuse = false;
5307 	  sf->current_sound = NULL;
5308 	  if (nxt_snd->copy) free_snd_data(nxt_snd);
5309 	}
5310       sf->cbi++;
5311       if (sf->cbi >= (sf->current_state)->size)
5312 	{
5313 	  reader_out_of_data(sf);
5314 	  return;
5315 	}
5316       sf->cb = FRAGMENT((sf->current_state), sf->cbi);
5317       if ((!(sf->cb)) ||
5318 	  (READER_SOUND(sf) == EDIT_LIST_END_MARK))
5319 	{
5320 	  reader_out_of_data(sf);
5321 	  return;
5322 	}
5323       ind0 = READER_LOCAL_POSITION(sf);
5324       ind1 = READER_LOCAL_END(sf);
5325       sf->fscaler = READER_SCALER(sf);
5326       if (zero_op(READER_TYPE(sf)))
5327 	{
5328 	  sf->current_sound = NULL;
5329 	  sf->loc = ind0;
5330 	  sf->first = ind0;
5331 	  sf->last = ind1;
5332 	  sf->data = NULL;
5333 	}
5334       else
5335 	{
5336 	  nxt_snd = sf->cp->sounds[READER_SOUND(sf)];
5337 	  if (nxt_snd->type == SND_DATA_FILE)
5338 	    {
5339 	      if (nxt_snd->inuse)
5340 		{
5341 		  nxt_snd = copy_snd_data(nxt_snd, ind0, FILE_BUFFER_SIZE);
5342 		  if (!nxt_snd)
5343 		    {
5344 		      reader_out_of_data(sf);
5345 		      return;
5346 		    }
5347 		}
5348 	      sf->data = nxt_snd->buffered_data;
5349 	      nxt_snd->inuse = true;
5350 	      sf->current_sound = nxt_snd;
5351 	      file_buffers_forward(ind0, ind1, ind0, sf, nxt_snd);
5352 	    }
5353 	  else
5354 	    {
5355 	      sf->data = nxt_snd->buffered_data;
5356 	      sf->loc = ind0;
5357 	      sf->first = ind0;
5358 	      sf->last = ind1;
5359 	    }
5360 	}
5361       sf->frag_pos = 0;
5362       choose_accessor(sf);
5363     }
5364   else
5365     {
5366       mus_long_t indx;
5367       ind0 = READER_LOCAL_POSITION(sf);
5368       ind1 = READER_LOCAL_END(sf);
5369       indx = io_end(sf->current_sound->io) + 1;
5370       file_buffers_forward(ind0, ind1, indx, sf, sf->current_sound);
5371     }
5372 }
5373 
5374 
next_sound(snd_fd * sf)5375 mus_float_t next_sound(snd_fd *sf)
5376 {
5377   next_sound_1(sf);
5378   return(read_sample(sf));
5379 }
5380 
5381 
copy_then_swap_channels(chan_info * cp0,chan_info * cp1,int pos0,int pos1)5382 void copy_then_swap_channels(chan_info *cp0, chan_info *cp1, int pos0, int pos1)
5383 {
5384   int i, fd, new0, new1;
5385   char *name;
5386   ed_list *new_ed, *old_ed;
5387   file_info *hdr0, *hdr1;
5388   peak_env_info *e0 = NULL, *e1 = NULL;
5389 
5390   if ((!(prepare_edit_list(cp0, AT_CURRENT_EDIT_POSITION, S_swap_channels))) ||
5391       (!(prepare_edit_list(cp1, AT_CURRENT_EDIT_POSITION, S_swap_channels))))
5392     return;
5393 
5394   name = cp0->sound->filename;
5395   hdr0 = copy_header(name, cp0->sound->hdr);
5396   fd = snd_open_read(name);
5397   snd_file_open_descriptors(fd,
5398 			    name,
5399 			    hdr0->sample_type,
5400 			    hdr0->data_location,
5401 			    hdr0->chans,
5402 			    hdr0->type);
5403   new0 = add_sound_file_to_edit_list(cp1, name,
5404 				     make_file_state(fd, hdr0, cp0->chan, 0, FILE_BUFFER_SIZE),
5405 				     hdr0, DONT_DELETE_ME, cp0->chan);
5406   name = cp1->sound->filename;
5407   hdr1 = copy_header(name, cp1->sound->hdr);
5408   fd = snd_open_read(name);
5409   snd_file_open_descriptors(fd,
5410 			    name,
5411 			    hdr1->sample_type,
5412 			    hdr1->data_location,
5413 			    hdr1->chans,
5414 			    hdr1->type);
5415   new1 = add_sound_file_to_edit_list(cp0, name,
5416 				     make_file_state(fd, hdr1, cp1->chan, 0, FILE_BUFFER_SIZE),
5417 				     hdr1, DONT_DELETE_ME, cp1->chan);
5418   e0 = peak_env_copy(cp0, false, pos0);
5419   if (e0) e1 = peak_env_copy(cp1, false, pos1);
5420   old_ed = cp1->edits[pos1];
5421   new_ed = make_ed_list(old_ed->size);
5422   new_ed->edit_type = CHANGE_EDIT;
5423   new_ed->sound_location = new1;
5424   new_ed->edpos = pos1;
5425   new_ed->maxamp = old_ed->maxamp;
5426   new_ed->maxamp_position = old_ed->maxamp_position;
5427   new_ed->samples = old_ed->samples;
5428   new_ed->cursor = old_ed->cursor;
5429   new_ed->beg = 0;
5430   new_ed->len = old_ed->len;
5431   new_ed->origin = mus_strdup(to_proc_name(S_swap_channels));
5432   cp0->edits[cp0->edit_ctr] = new_ed;
5433   if (new_ed->len > 0)
5434     for (i = 0; i < new_ed->size; i++)
5435       {
5436 	copy_ed_fragment(FRAGMENT(new_ed, i), FRAGMENT(old_ed, i));
5437 	if (FRAGMENT_SOUND(new_ed, i) == 0) FRAGMENT_SOUND(new_ed, i) = new1;
5438       }
5439   old_ed = cp0->edits[pos0];
5440   new_ed = make_ed_list(old_ed->size);
5441   new_ed->edit_type = CHANGE_EDIT;
5442   new_ed->sound_location = new0;
5443   new_ed->edpos = pos0;
5444   new_ed->maxamp = old_ed->maxamp;
5445   new_ed->maxamp_position = old_ed->maxamp_position;
5446   new_ed->beg = 0;
5447   new_ed->len = old_ed->len;
5448   new_ed->samples = old_ed->samples;
5449   new_ed->cursor = old_ed->cursor;
5450   new_ed->origin = mus_strdup(to_proc_name(S_swap_channels)); /* swap = stored change-edit at restore time, so no redundancy here */
5451   cp1->edits[cp1->edit_ctr] = new_ed;
5452   if (new_ed->len > 0)
5453     for (i = 0; i < new_ed->size; i++)
5454       {
5455 	copy_ed_fragment(FRAGMENT(new_ed, i), FRAGMENT(old_ed, i));
5456 	if (FRAGMENT_SOUND(new_ed, i) == 0) FRAGMENT_SOUND(new_ed, i) = new0;
5457       }
5458   if ((e0) && (e1))
5459     {
5460       cp0->edits[cp0->edit_ctr]->peak_env = e1;
5461       cp1->edits[cp1->edit_ctr]->peak_env = e0;
5462     }
5463   else
5464     {
5465       if (e0) free_peak_env_info(e0);
5466       if (e1) free_peak_env_info(e1);
5467     }
5468 
5469   ripple_all(cp0, 0, 0);
5470   ripple_all(cp1, 0, 0);
5471   swap_marks(cp0, cp1);
5472   if (cp0->edits[cp0->edit_ctr]->samples != cp0->edits[cp0->edit_ctr - 1]->samples)
5473     reflect_sample_change_in_axis(cp0);
5474   if (cp1->edits[cp1->edit_ctr]->samples != cp1->edits[cp1->edit_ctr - 1]->samples)
5475     reflect_sample_change_in_axis(cp1);
5476   after_edit(cp0);
5477   after_edit(cp1);
5478   update_graph(cp0);
5479   update_graph(cp1);
5480 }
5481 
5482 
save_edits_and_update_display(snd_info * sp)5483 io_error_t save_edits_and_update_display(snd_info *sp)
5484 {
5485   /* open temp, write current state, rename to old, reopen and clear all state */
5486   /* can't overwrite current because we may have cut/paste backpointers scattered around the current edit list */
5487   /* have to decide here what header/data type to write as well -- original? */
5488   /* if latter, must be able to write all headers! -- perhaps warn user and use snd/aiff/riff/ircam */
5489 
5490   /* read_only already checked */
5491   char *ofile = NULL;
5492   int i;
5493   mus_long_t samples = 0;
5494   mus_long_t *old_cursors = NULL;
5495   void *ms;
5496   axes_data *sa;
5497   file_info *sphdr = NULL;
5498   io_error_t io_err = IO_NO_ERROR;
5499   snd_fd **sf;
5500   mus_float_t *vals = NULL;
5501   mus_long_t *times = NULL;
5502   bool have_maxamps = true;
5503 
5504   if (dont_save(sp, NULL)) return(IO_SAVE_HOOK_CANCELLATION);
5505   ofile = snd_tempnam();
5506   /* this will use user's TMPDIR if temp_dir(ss) is not set, else stdio.h's P_tmpdir else /tmp */
5507 
5508   for (i = 0; i < (int)sp->nchans; i++)
5509     {
5510       chan_info *ncp;
5511       ncp = sp->chans[i];
5512       if ((ed_maxamp(ncp, ncp->edit_ctr) < 0.0) ||
5513 	  (ed_maxamp_position(ncp, ncp->edit_ctr) < 0))
5514 	{
5515 	  have_maxamps = false;
5516 	  break;
5517 	}
5518     }
5519   if (have_maxamps)
5520     {
5521       vals = (mus_float_t *)calloc(sp->nchans, sizeof(mus_float_t));
5522       times = (mus_long_t *)calloc(sp->nchans, sizeof(mus_long_t));
5523       for (i = 0; i < (int)sp->nchans; i++)
5524 	{
5525 	  chan_info *ncp;
5526 	  ncp = sp->chans[i];
5527 	  vals[i] = ed_maxamp(ncp, ncp->edit_ctr);
5528 	  times[i] = ed_maxamp_position(ncp, ncp->edit_ctr);
5529 	}
5530     }
5531 
5532   sf = (snd_fd **)calloc(sp->nchans, sizeof(snd_fd *));
5533   for (i = 0; i < (int)sp->nchans; i++)
5534     {
5535       sf[i] = init_sample_read(0, sp->chans[i], READ_FORWARD);
5536       if (!sf[i])
5537 	{
5538 	  int j;
5539 	  for (j = 0; j < i; j++) free_snd_fd(sf[j]);
5540 	  free(sf);
5541 	  if (vals) free(vals);
5542 	  if (times) free(times);
5543 	  return(IO_BAD_CHANNEL);
5544 	}
5545       if (samples < current_samples(sp->chans[i]))
5546 	samples = current_samples(sp->chans[i]);
5547     }
5548 
5549   /* write the new file */
5550   io_err = snd_make_file(ofile, sp->nchans, sp->hdr, sf, samples, true);
5551   for (i = 0; i < (int)sp->nchans; i++) free_snd_fd(sf[i]);
5552   free(sf);
5553   sf = NULL;
5554   if (io_err != IO_NO_ERROR)
5555     {
5556       if (ofile) free(ofile);
5557       if (vals)
5558 	{
5559 	  free(vals);
5560 	  free(times);
5561 	}
5562       return(io_err);
5563     }
5564 
5565   sa = make_axes_data(sp);
5566   sphdr = sp->hdr;
5567   sphdr->samples = samples * sp->nchans;
5568   ms = (void *)sound_store_marks(sp);
5569   old_cursors = (mus_long_t *)calloc(sp->nchans, sizeof(mus_long_t));
5570   for (i = 0; i < (int)sp->nchans; i++)
5571     {
5572       chan_info *cp;
5573       cp = sp->chans[i];
5574       old_cursors[i] = cursor_sample(cp);        /* depends on edit_ctr -- set to -1 by free_edit_list below */
5575       if (ss->deferred_regions > 0)
5576 	sequester_deferred_regions(cp, -1);
5577       if (cp->edits) free_edit_list(cp); /* sets cp->edits to NULL */
5578       if (cp->sounds) free_sound_list(cp);
5579       cp->axis = free_axis_info(cp->axis);
5580     }
5581 
5582 #ifndef _MSC_VER
5583   if (access(sp->filename, W_OK))
5584     {
5585       free_axes_data(sa);
5586       if (ofile) free(ofile);
5587       if (old_cursors) free(old_cursors);
5588       if (vals)
5589 	{
5590 	  free(vals);
5591 	  free(times);
5592 	}
5593       return(IO_WRITE_PROTECTED);
5594     }
5595 #endif
5596 
5597   mus_sound_forget(sp->filename);
5598   sp->writing = true;
5599   io_err = move_file(ofile, sp->filename); /* should we cancel and restart a monitor? */
5600   sp->writing = false;
5601   if (io_err != IO_NO_ERROR)
5602     {
5603       if (ofile) free(ofile);
5604       if (old_cursors) free(old_cursors);
5605       if (vals)
5606 	{
5607 	  free(vals);
5608 	  free(times);
5609 	}
5610       return(io_err);
5611     }
5612 
5613   if (vals)
5614     {
5615       mus_sound_set_maxamps(sp->filename, sp->nchans, vals, times);
5616       free(vals);
5617       free(times);
5618     }
5619   sp->write_date = file_write_date(sp->filename);
5620   add_sound_data(sp->filename, sp, WITHOUT_INITIAL_GRAPH_HOOK);
5621   restore_axes_data(sp, sa, mus_sound_duration(sp->filename), true);
5622   sound_restore_marks(sp, ms);
5623   free_axes_data(sa);
5624   for (i = 0; i < (int)sp->nchans; i++)
5625     cursor_sample(sp->chans[i]) = old_cursors[i];
5626   free(old_cursors);
5627   reflect_file_revert_in_label(sp);
5628   if (ofile)
5629     {
5630       free(ofile);
5631       ofile = NULL;
5632     }
5633   if (!(ss->file_monitor_ok))
5634     if (auto_update(ss))
5635       for_each_sound(sound_not_current);
5636   return(IO_NO_ERROR);
5637 }
5638 
5639 
save_edits_without_display(snd_info * sp,const char * new_name,mus_header_t type,mus_sample_t sample_type,int srate,const char * comment,int pos)5640 io_error_t save_edits_without_display(snd_info *sp, const char *new_name, mus_header_t type,
5641 				      mus_sample_t sample_type, int srate, const char *comment, int pos)
5642 {
5643   /* assume we've already checked for (over)write permissions, and header-type+sample-type writable,
5644    */
5645   file_info *hdr;
5646   snd_fd **sf;
5647   mus_long_t framples = 0;
5648   int i;
5649   file_info *ohdr;
5650   io_error_t err = IO_NO_ERROR;
5651   mus_float_t *vals = NULL;
5652   mus_long_t *times = NULL;
5653   bool have_maxamps = true;
5654 
5655   if (dont_save(sp, new_name))
5656     return(IO_SAVE_HOOK_CANCELLATION);
5657 
5658   ohdr = sp->hdr;
5659   hdr = copy_header(new_name, ohdr);
5660   hdr->sample_type = sample_type;
5661   hdr->srate = srate;
5662   hdr->type = type;
5663   if (comment)
5664     hdr->comment = mus_strdup(comment);
5665   else hdr->comment = NULL;
5666   hdr->data_location = 0; /* in case comment changes it */
5667 
5668   for (i = 0; i < (int)sp->nchans; i++)
5669     {
5670       chan_info *ncp;
5671       ncp = sp->chans[i];
5672       if ((ed_maxamp(ncp, ncp->edit_ctr) < 0.0) ||
5673 	  (ed_maxamp_position(ncp, ncp->edit_ctr) < 0))
5674 	{
5675 	  have_maxamps = false;
5676 	  break;
5677 	}
5678     }
5679   if (have_maxamps)
5680     {
5681       vals = (mus_float_t *)calloc(sp->nchans, sizeof(mus_float_t));
5682       times = (mus_long_t *)calloc(sp->nchans, sizeof(mus_long_t));
5683       for (i = 0; i < (int)sp->nchans; i++)
5684 	{
5685 	  chan_info *ncp;
5686 	  ncp = sp->chans[i];
5687 	  vals[i] = ed_maxamp(ncp, ncp->edit_ctr);
5688 	  times[i] = ed_maxamp_position(ncp, ncp->edit_ctr);
5689 	}
5690     }
5691 
5692   sf = (snd_fd **)malloc(sp->nchans * sizeof(snd_fd *));
5693   for (i = 0; i < (int)sp->nchans; i++)
5694     {
5695       chan_info *cp;
5696       int local_pos;
5697       cp = sp->chans[i];
5698       if (pos == AT_CURRENT_EDIT_POSITION) local_pos = cp->edit_ctr; else local_pos = pos;
5699       if (framples < cp->edits[local_pos]->samples) framples = cp->edits[local_pos]->samples;
5700       sf[i] = init_sample_read_any(0, cp, READ_FORWARD, local_pos);
5701       if (!sf[i])
5702 	{
5703 	  uint32_t k;
5704 	  /* this should not (cannot?) happen since we've supposedly checked before getting here... */
5705 	  for (k = 0; k < sp->nchans; k++)
5706 	    sf[k] = free_snd_fd(sf[k]);
5707 	  free(sf);
5708 	  sf = NULL;
5709 	  free_file_info(hdr);
5710 	  if (vals)
5711 	    {
5712 	      free(vals);
5713 	      free(times);
5714 	    }
5715 	  return(err);
5716 	}
5717     }
5718 
5719   err = snd_make_file(new_name, sp->nchans, hdr, sf, framples, true);
5720   if (vals)
5721     {
5722       if (err == IO_NO_ERROR)
5723 	mus_sound_set_maxamps(new_name, sp->nchans, vals, times);
5724       free(vals);
5725       free(times);
5726     }
5727 
5728   for (i = 0; i < (int)sp->nchans; i++)
5729     free_snd_fd(sf[i]);
5730   free(sf);
5731   free_file_info(hdr);
5732 
5733   return(err);
5734 }
5735 
5736 
save_channel_edits(chan_info * cp,const char * ofile,int pos)5737 io_error_t save_channel_edits(chan_info *cp, const char *ofile, int pos)
5738 {
5739   /* channel extraction -- does not (normally) cause reversion of edits, or change of in-window file, etc */
5740   snd_info *sp;
5741   io_error_t err = IO_NO_ERROR;
5742   sp = cp->sound;
5743   if (pos == AT_CURRENT_EDIT_POSITION)
5744     pos = cp->edit_ctr;
5745   if (mus_strcmp(ofile, sp->filename))        /* overwriting current file with one of its channels */
5746     {
5747       char *nfile = NULL;
5748       if ((sp->user_read_only == FILE_READ_ONLY) ||
5749 	  (sp->file_read_only == FILE_READ_ONLY))
5750 	{
5751 	  snd_error("can't save channel as %s (%s is write-protected)", ofile, sp->short_filename);
5752 	  return(IO_WRITE_PROTECTED);
5753 	}
5754       nfile = snd_tempnam();
5755       err = channel_to_file(cp, nfile, pos);  /* snd_error unless MUS_INTERRUPTED (???) */
5756       if (err == IO_NO_ERROR)
5757 	{
5758 	  err = move_file(nfile, ofile);
5759 	  if (err == IO_NO_ERROR)
5760 	    snd_update(sp);
5761 	  else
5762 	    {
5763 	      if (is_serious_io_error(err))
5764 		{
5765 		  char buf[1024];
5766 		  snprintf(buf, 1024, "save channel %s -> %s: %s (%s)",
5767 			   nfile, ofile,
5768 			   io_error_name(err),
5769 			   snd_io_strerror());
5770 		  free(nfile);
5771 		  snd_error("%s", buf);
5772 		}
5773 	    }
5774 	}
5775       if (nfile) free(nfile);
5776     }
5777   else err = channel_to_file(cp, ofile, pos); /* snd_error unless MUS_INTERRUPTED */
5778   return(err);
5779 }
5780 
5781 
has_unsaved_edits(snd_info * sp)5782 bool has_unsaved_edits(snd_info *sp)
5783 {
5784   uint32_t i;
5785   for (i = 0; i < sp->nchans; i++)
5786     if (sp->chans[i]->edit_ctr > 0)
5787       return(true);
5788   return(false);
5789 }
5790 
5791 
save_edits_1(snd_info * sp,bool ask)5792 static io_error_t save_edits_1(snd_info *sp, bool ask)
5793 {
5794   io_error_t err;
5795   time_t current_write_date;
5796 
5797   if (!sp)
5798     snd_error_without_format("save edits of null sound!");
5799   if ((sp->user_read_only == FILE_READ_ONLY) ||
5800       (sp->file_read_only == FILE_READ_ONLY))
5801     return(IO_WRITE_PROTECTED);
5802 
5803   if (!(has_unsaved_edits(sp))) return(IO_NO_CHANGES);
5804 
5805   /* check for change to file while we were editing it */
5806   current_write_date = file_write_date(sp->filename);
5807   /* returns -1 if file does not exist (stat -> -1) */
5808 
5809   if (current_write_date < 0)
5810     {
5811       snd_error("can't save edits; %s has disappeared!", sp->filename);
5812       /* unless by chance it fits in one in-core buffer, there's nothing we can do now */
5813       return(IO_CANT_OPEN_FILE);
5814     }
5815   if ((ask) &&
5816       ((current_write_date - sp->write_date) > 1)) /* In Redhat 7.1 these can differ by 1?? Surely this is a bug! */
5817     return(IO_NEED_WRITE_CONFIRMATION);            /* see snd-kbd.c save_edits_from_kbd for the rest of this */
5818 
5819   /* this used to include ask_before_overwrite, but I don't think that is relevant.  If the file
5820    *    has changed from what we think it is, we have a problem -- the save is unlikely to do anything useful.
5821    */
5822 
5823   err = save_edits_and_update_display(sp);
5824 
5825   if (err == IO_NO_ERROR)
5826     {
5827       if (sp->edited_region)
5828 	save_region_backpointer(sp); /* region edit save is not reflected in other sounds because the region is a separate thing from the selection */
5829     }
5830   return(err);
5831 }
5832 
5833 
save_edits(snd_info * sp)5834 io_error_t save_edits(snd_info *sp)
5835 {
5836   return(save_edits_1(sp, true));
5837 }
5838 
5839 
save_edits_without_asking(snd_info * sp)5840 io_error_t save_edits_without_asking(snd_info *sp)
5841 {
5842   return(save_edits_1(sp, false));
5843 }
5844 
5845 
revert_edits(chan_info * cp)5846 void revert_edits(chan_info *cp)
5847 {
5848   if (cp->edit_ctr == 0) return;
5849   cp->edit_ctr = 0;
5850   clear_transform_edit_ctrs(cp);
5851   reflect_edit_counter_change(cp);
5852   reflect_sample_change_in_axis(cp);
5853   enved_reflect_selection(selection_is_active());
5854   reflect_selection_in_save_as_dialog(selection_is_active());
5855 
5856   if (Xen_hook_has_list(ss->effects_hook))
5857     run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
5858 
5859   update_graph(cp);
5860   reflect_mix_change(ANY_MIX_ID);
5861   reflect_enved_fft_change(cp);
5862   if ((cp->hookable == WITH_HOOK) &&
5863       (Xen_is_hook(cp->undo_hook)) &&
5864       (Xen_hook_has_list(cp->undo_hook)))
5865     run_hook(cp->undo_hook, Xen_empty_list, S_undo_hook);
5866 }
5867 
5868 
5869 /* how to handle something like safe-map-channel that wants to disallow undo? */
5870 
undo_edit(chan_info * cp,int count)5871 bool undo_edit(chan_info *cp, int count)
5872 {
5873   if ((cp) &&
5874       (cp->edit_ctr > 0) &&
5875       (count != 0))
5876     {
5877       snd_info *sp;
5878       sp = cp->sound;
5879       cp->edit_ctr -= count;
5880       if (cp->edit_ctr < 0) cp->edit_ctr = 0;
5881       clear_transform_edit_ctrs(cp);
5882       reflect_edit_counter_change(cp);
5883       reflect_sample_change_in_axis(cp);
5884       enved_reflect_selection(selection_is_active());
5885       reflect_selection_in_save_as_dialog(selection_is_active());
5886       if (cp->edit_ctr == 0)
5887 	{
5888 	  reflect_file_revert_in_label(sp);
5889 	}
5890 
5891       if (Xen_hook_has_list(ss->effects_hook))
5892 	run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
5893 
5894       update_graph(cp);
5895       reflect_mix_change(ANY_MIX_ID);
5896       reflect_enved_fft_change(cp);
5897       if ((cp->hookable == WITH_HOOK) &&
5898 	  (Xen_is_hook(cp->undo_hook)) &&
5899 	  (Xen_hook_has_list(cp->undo_hook)))
5900 	run_hook(cp->undo_hook, Xen_empty_list, S_undo_hook);
5901       return(true);
5902     }
5903   return(false);
5904 }
5905 
5906 
undo_edit_with_sync(chan_info * cp,int count)5907 bool undo_edit_with_sync(chan_info *cp, int count)
5908 {
5909   /* there is a problem with syncd undo: if the syncd edit decided one portion
5910    *   was a no-op (scale by 1.0 etc), but not another, a subsequent undo with
5911    *   sync can end up in a state different from where it started.
5912    */
5913   if (count == 0) return(false);
5914   if (count < 0)
5915     return(redo_edit_with_sync(cp, -count));
5916   else
5917     {
5918       if (cp)
5919 	{
5920 	  snd_info *sp;
5921 	  sync_info *si = NULL;
5922 	  sp = cp->sound;
5923 	  if (sp->sync != 0) si = snd_sync(sp->sync);
5924 	  if (si)
5925 	    {
5926 	      bool something_changed = false;
5927 	      int i;
5928 	      for (i = 0; i < si->chans; i++)
5929 		if (undo_edit(si->cps[i], count))
5930 		  something_changed = true;
5931 	      free_sync_info(si);
5932 	      return(something_changed);
5933 	    }
5934 	  else return(undo_edit(cp, count));
5935 	}
5936     }
5937   return(false);
5938 }
5939 
5940 
redo_edit(chan_info * cp,int count)5941 bool redo_edit(chan_info *cp, int count)
5942 {
5943   /* returns true if an edit history change occurred */
5944   if ((cp) && (count != 0))
5945     {
5946       int old_edit_ctr;
5947       old_edit_ctr = cp->edit_ctr;
5948       cp->edit_ctr += count;
5949       if (cp->edit_ctr >= cp->edit_size) cp->edit_ctr = cp->edit_size - 1;
5950       while (!(cp->edits[cp->edit_ctr]))
5951 	cp->edit_ctr--;
5952       if ((cp->edit_ctr != 0) &&          /* possibly a sync'd redo to chan that has no edits */
5953 	  (cp->edit_ctr != old_edit_ctr)) /* or attempt to redo when nothing to redo */
5954 	{
5955 	  clear_transform_edit_ctrs(cp);
5956 	  reflect_file_change_in_label(cp);
5957 	  reflect_edit_counter_change(cp);
5958 	  reflect_sample_change_in_axis(cp);
5959 	  enved_reflect_selection(selection_is_active());
5960 	  reflect_selection_in_save_as_dialog(selection_is_active());
5961 
5962 	  if (Xen_hook_has_list(ss->effects_hook))
5963 	    run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
5964 
5965 	  update_graph(cp);
5966 	  reflect_mix_change(ANY_MIX_ID);
5967 	  reflect_enved_fft_change(cp);
5968 	  if ((cp->hookable == WITH_HOOK) &&
5969 	      (Xen_is_hook(cp->undo_hook)) &&
5970 	      (Xen_hook_has_list(cp->undo_hook)))
5971 	    run_hook(cp->undo_hook, Xen_empty_list, S_undo_hook);
5972 	  return(true);
5973 	}
5974     }
5975   return(false);
5976 }
5977 
5978 
redo_edit_with_sync(chan_info * cp,int count)5979 bool redo_edit_with_sync(chan_info *cp, int count)
5980 {
5981   if (count == 0) return(false);
5982   if (count < 0)
5983     return(undo_edit_with_sync(cp, -count));
5984   else
5985     {
5986       if (cp)
5987 	{
5988 	  snd_info *sp;
5989 	  sync_info *si = NULL;
5990 	  sp = cp->sound;
5991 	  if (sp->sync != 0) si = snd_sync(sp->sync);
5992 	  if (si)
5993 	    {
5994 	      bool something_changed = false;
5995 	      int i;
5996 	      for (i = 0; i < si->chans; i++)
5997 		if (redo_edit(si->cps[i], count))
5998 		  something_changed = true;
5999 	      free_sync_info(si);
6000 	      return(something_changed);
6001 	    }
6002 	  else return(redo_edit(cp, count));
6003 	}
6004     }
6005   return(false);
6006 }
6007 
6008 
6009 
6010 /* -------------------- virtual mixes -------------------- */
6011 
6012 /* ramp on mix is technically possible, but ambiguous -- currently if we mix, then ramp elsewhere, then drag the mix
6013  *   to the ramped portion, the mix treats the ramp as prior data (adding);  if we had ramp_mix, it would want to
6014  *   treat that ramp as an envelope on the mix if the ramp happened after the mix was established but as data if before.
6015  *   Too tricky.  (We'd also need ED_RAMP_ZERO to make clean fixups upon drag and so on).
6016  *   Three times and out so far...
6017  *   ...
6018  *   but, if the envelope is global, it is not ambiguous, (except that we have scaled portions?) --
6019  *     still the ramp-but-no-mix sections need special handling (ramp_mix with 0 mixes?),
6020  *     as do the ramp-as-scale-but-no-mix portions (scale_mix? -- confusing name)
6021  *   ...
6022  *   this (ramp_mix as edit with locked mixes) turned out to be too tricky -- scaling especially is a mess.
6023  *   (scaling can't be globalized without saving the pre-mix scaler on every mix, for a start)
6024  */
6025 
make_mix_fragment(ed_list * new_ed,int i,mix_state * ms)6026 static void make_mix_fragment(ed_list *new_ed, int i, mix_state *ms)
6027 {
6028   ed_mixes *mxs;
6029   int mloc = -1;
6030 
6031   /* mix_loc = index into cp->sound arrays for reading the mixed-in data (buffer or file) */
6032   /* i = index into ed_fragment list for this edit */
6033   /* we're changing one fragment to add the mix */
6034 
6035   FRAGMENT_TYPE(new_ed, i) = type_info[FRAGMENT_TYPE(new_ed, i)].add_mix;
6036   if (!(FRAGMENT_MIXES(new_ed, i)))
6037     FRAGMENT_MIXES(new_ed, i) = (ed_mixes *)calloc(1, sizeof(ed_mixes));
6038   mxs = FRAGMENT_MIXES(new_ed, i);
6039   if (mxs->size == 0)
6040     {
6041       mxs->size = 1;
6042       mxs->mix_list = (mix_state **)malloc(sizeof(mix_state *));
6043       mloc = 0;
6044     }
6045   else
6046     {
6047       int j;
6048       for (j = 0; j < mxs->size; j++)
6049 	if (!(MIX_LIST_STATE(mxs, j)))
6050 	  {
6051 	    mloc = j;
6052 	    break;
6053 	  }
6054       if (mloc == -1)
6055 	{
6056 	  mloc = mxs->size;
6057 	  mxs->size++;
6058 	  mxs->mix_list = (mix_state **)realloc(mxs->mix_list, mxs->size * sizeof(mix_state *));
6059 	}
6060     }
6061   MIX_LIST_STATE(mxs, mloc) = ms;
6062 }
6063 
6064 
make_mix_edit(ed_list * old_ed,mus_long_t beg,mus_long_t len,mix_state * ms,bool full_fragment)6065 static ed_list *make_mix_edit(ed_list *old_ed, mus_long_t beg, mus_long_t len, mix_state *ms, bool full_fragment)
6066 {
6067   ed_list *new_ed;
6068   int i;
6069   if (full_fragment)
6070     {
6071       new_ed = make_ed_list(old_ed->size);
6072       new_ed->beg = 0;
6073       new_ed->len = len;
6074       for (i = 0; i < new_ed->size; i++) /* whole file changed in this case */
6075 	{
6076 	  copy_ed_fragment(FRAGMENT(new_ed, i), FRAGMENT(old_ed, i));
6077 	  if (i < new_ed->size - 1)
6078 	    make_mix_fragment(new_ed, i, ms);
6079 	}
6080     }
6081   else
6082     {
6083       new_ed = copy_and_split_list(beg, len, old_ed);
6084       for (i = 0; i < new_ed->size; i++)
6085 	{
6086 	  if (FRAGMENT_GLOBAL_POSITION(new_ed, i) > (beg + len - 1))
6087 	    break;                                                    /* not >= (1 sample selections) */
6088 	  if ((FRAGMENT_GLOBAL_POSITION(new_ed, i) >= beg) &&
6089 	      (i < new_ed->size - 1))
6090 	    make_mix_fragment(new_ed, i, ms);
6091 	}
6092     }
6093 
6094   new_ed->cursor = old_ed->cursor;
6095   new_ed->edit_type = MIX_EDIT;
6096   new_ed->sound_location = old_ed->sound_location;
6097   new_ed->selection_beg = old_ed->selection_beg;
6098   new_ed->selection_end = old_ed->selection_end;
6099   return(new_ed);
6100 }
6101 
6102 
mix_file_with_tag(chan_info * cp,const char * filename,int chan,mus_long_t beg,file_delete_t auto_delete,const char * origin)6103 int mix_file_with_tag(chan_info *cp, const char *filename, int chan, mus_long_t beg, file_delete_t auto_delete, const char *origin)
6104 {
6105   mus_long_t file_len, old_len, new_len;
6106   int edpos;
6107   int fd, mix_loc;
6108   ed_list *new_ed, *old_ed;
6109   file_info *hdr;
6110   mix_state *ms;
6111   bool backup = false;
6112 
6113   hdr = make_file_info(filename, FILE_READ_ONLY, FILE_NOT_SELECTED);
6114   if (chan >= hdr->chans)
6115     return(NO_MIX_TAG);
6116 
6117   edpos = cp->edit_ctr;
6118   file_len = mus_sound_framples(filename);
6119   old_ed = cp->edits[edpos];
6120 
6121   if ((beg < 0) ||
6122       (file_len <= 0))
6123     return(NO_MIX_TAG);
6124 
6125   old_len = old_ed->samples;
6126   if (beg + file_len > old_len)
6127     {
6128       if (!(extend_with_zeros(cp, old_len, beg + file_len - old_len, edpos, origin)))
6129 	return(NO_MIX_TAG);
6130       edpos = cp->edit_ctr;
6131       new_len = beg + file_len;
6132       old_ed = cp->edits[edpos];
6133       backup = true;
6134     }
6135   else new_len = old_len;
6136 
6137   if (!(prepare_edit_list(cp, edpos, origin)))
6138     return(NO_MIX_TAG);
6139 
6140   fd = snd_open_read(filename);
6141   snd_file_open_descriptors(fd,
6142 			    filename,
6143 			    hdr->sample_type,
6144 			    hdr->data_location,
6145 			    hdr->chans,
6146 			    hdr->type);
6147 
6148   mix_loc = add_sound_file_to_edit_list(cp, filename,
6149 					make_file_state(fd, hdr, chan, 0, FILE_BUFFER_SIZE),
6150 					hdr, auto_delete,
6151 					chan);
6152 
6153   ms = prepare_mix_state_for_channel(cp, mix_loc, beg, file_len);
6154   new_ed = make_mix_edit(old_ed, beg, file_len, ms, ((beg == 0) && (file_len >= old_len)));
6155   new_ed->samples = new_len;
6156   new_ed->origin = mus_strdup(origin);
6157   new_ed->edpos = edpos;
6158   cp->edits[cp->edit_ctr] = new_ed;
6159   add_ed_mix(cp->edits[cp->edit_ctr], ms);
6160 
6161   ripple_all(cp, 0, 0); /* 0,0 -> copy marks */
6162   reflect_mix_change(ANY_MIX_ID);
6163   after_edit(cp);
6164 
6165   if (backup)
6166     backup_edit_list(cp);
6167 
6168   return(ms->mix_id);
6169 }
6170 
6171 
mix_buffer_with_tag(chan_info * cp,mus_float_t * data,mus_long_t beg,mus_long_t buf_len,const char * origin)6172 int mix_buffer_with_tag(chan_info *cp, mus_float_t *data, mus_long_t beg, mus_long_t buf_len, const char *origin)
6173 {
6174   int edpos, mix_loc;
6175   mus_long_t old_len, new_len;
6176   ed_list *old_ed, *new_ed;
6177   bool backup = false;
6178   mix_state *ms;
6179 
6180   edpos = cp->edit_ctr;
6181   old_ed = cp->edits[edpos];
6182 
6183   if ((beg < 0) ||
6184       (buf_len == 0))
6185     return(NO_MIX_TAG);
6186 
6187   old_len = old_ed->samples;
6188   if (beg + buf_len > old_len)
6189     {
6190       if (!(extend_with_zeros(cp, old_len, beg + buf_len - old_len, edpos, origin)))
6191 	return(NO_MIX_TAG);
6192       edpos = cp->edit_ctr;
6193       new_len = beg + buf_len;
6194       old_ed = cp->edits[edpos];
6195       backup = true;
6196     }
6197   else new_len = old_len;
6198 
6199   if (!(prepare_edit_list(cp, edpos, origin)))
6200     return(NO_MIX_TAG);
6201 
6202   prepare_sound_list(cp);
6203   cp->sounds[cp->sound_ctr] = make_snd_data_buffer(data, (int)buf_len, cp->edit_ctr);
6204   mix_loc = cp->sound_ctr;
6205 
6206   ms = prepare_mix_state_for_channel(cp, mix_loc, beg, buf_len);
6207   new_ed = make_mix_edit(old_ed, beg, buf_len, ms, ((beg == 0) && (buf_len >= old_len)));
6208   new_ed->samples = new_len;
6209   new_ed->origin = mus_strdup(origin);
6210   new_ed->edpos = edpos;
6211   cp->edits[cp->edit_ctr] = new_ed;
6212   add_ed_mix(cp->edits[cp->edit_ctr], ms);
6213 
6214   ripple_all(cp, 0, 0); /* 0,0 -> copy marks */
6215   reflect_mix_change(ANY_MIX_ID);
6216   after_edit(cp);
6217 
6218   if (backup)
6219     backup_edit_list(cp);
6220 
6221   return(ms->mix_id);
6222 }
6223 
6224 
unmix(chan_info * cp,mix_state * ms)6225 void unmix(chan_info *cp, mix_state *ms)
6226 {
6227   /* assume both mix_list (via ripple) and ed fragments list are ready for the unmix */
6228   /* used in set position and set speed (snd-mix) to remove ms prior to the edit/remix */
6229   int i;
6230   ed_list *ed;
6231   ed = cp->edits[cp->edit_ctr];
6232   for (i = 0; i < ed->size; i++)
6233     {
6234       if ((FRAGMENT_GLOBAL_POSITION(ed, i) >= ms->beg) &&
6235 	  (FRAGMENT_GLOBAL_POSITION(ed, i) < (ms->beg + ms->len)) &&
6236 	  (is_unmixable_op(FRAGMENT_TYPE(ed, i))))
6237 	{
6238 	  /* look for ms in the current fragment's mix list */
6239 	  int j, remaining_mixes = 0, mss_size;
6240 	  ed_mixes *mxl;
6241 	  mix_state **mss;
6242 	  mxl = FRAGMENT_MIXES(ed, i);
6243 	  mss = FRAGMENT_MIX_LIST(ed, i);
6244 	  mss_size = FRAGMENT_MIX_LIST_SIZE(ed, i);
6245 	  for (j = 0; j < mss_size; j++)
6246 	    if (mss[j])
6247 	      {
6248 		if (mss[j]->index == ms->index)
6249 		  mss[j] = NULL;
6250 		else remaining_mixes++;
6251 	      }
6252 	  if (remaining_mixes == 0)
6253 	    {
6254 	      FRAGMENT_TYPE(ed, i) = type_info[FRAGMENT_TYPE(ed, i)].subtract_mix;
6255 	      free(mss);
6256 	      free(mxl);
6257 	      FRAGMENT_MIXES(ed, i) = NULL;
6258 	    }
6259 	}
6260     }
6261 }
6262 
6263 
remix(chan_info * cp,mix_state * ms)6264 void remix(chan_info *cp, mix_state *ms)
6265 {
6266   int i;
6267   ed_list *new_ed;
6268   new_ed = cp->edits[cp->edit_ctr];
6269   for (i = 0; i < new_ed->size; i++)
6270     {
6271       if (FRAGMENT_GLOBAL_POSITION(new_ed, i) > (ms->beg + ms->len - 1))
6272 	break;                                                    /* not >= (1 sample selections) */
6273       if (FRAGMENT_GLOBAL_POSITION(new_ed, i) >= ms->beg)
6274 	make_mix_fragment(new_ed, i, ms);
6275     }
6276 }
6277 
6278 
ripple_mixes_1(chan_info * cp,mus_long_t beg,mus_long_t len,mus_long_t change,mus_float_t scl)6279 static void ripple_mixes_1(chan_info *cp, mus_long_t beg, mus_long_t len, mus_long_t change, mus_float_t scl)
6280 {
6281   /* this is where most of the time goes in mixing! */
6282   if ((cp) &&
6283       (cp->edit_ctr > 0))
6284     {
6285       ed_list *ed;
6286       int i, low_id = 0, high_id, size = 0; /* low_id confuses the compiler, but it will always get set below (current_states starts NULL etc) */
6287       mix_state **current_states = NULL;
6288 
6289       ed = cp->edits[cp->edit_ctr];
6290       /* this may have mixes already (current op was mix op) or might be null */
6291       for (i = 0; i < ed->size; i++)
6292 	{
6293 	  if ((FRAGMENT_MIXES(ed, i)) &&
6294 	      (FRAGMENT_MIX_LIST(ed, i)) &&
6295 	      (FRAGMENT_MIX_LIST_SIZE(ed, i) > 0))
6296 	    {
6297 	      int j;
6298 	      if (!current_states)
6299 		{
6300 		  low_id = lowest_mix_id();
6301 		  high_id = highest_mix_id();
6302 		  size = high_id - low_id + 1;
6303 		  current_states = (mix_state **)calloc(size, sizeof(mix_state *));
6304 		  preload_mixes(current_states, low_id, ed);
6305 		}
6306 	      for (j = 0; j < FRAGMENT_MIX_LIST_SIZE(ed, i); j++)
6307 		{
6308 		  mix_state *old_ms;
6309 		  old_ms = FRAGMENT_MIX_STATE(ed, i, j);            /* the old copy -- we (may) need to make a new one */
6310 		  if (old_ms)
6311 		    {
6312 		      mix_state *new_ms;
6313 		      new_ms = current_states[old_ms->mix_id - low_id];
6314 		      if (!new_ms)
6315 			{
6316 			  new_ms = copy_mix_state(old_ms); /* cannot return null unless we're out of memory */
6317 			  add_ed_mix(ed, new_ms);
6318 			  if (new_ms->beg >= beg)
6319 			    {
6320 			      if ((len != 0) &&
6321 				  (new_ms->beg < beg + len))
6322 				new_ms->scaler *= scl;
6323 			      if (change != 0)
6324 				new_ms->beg += change;
6325 			    }
6326 			}
6327 		      FRAGMENT_MIX_STATE(ed, i, j) = new_ms;
6328 		      if (((new_ms->mix_id - low_id) < size) &&
6329 			  ((new_ms->mix_id - low_id) >= 0))
6330 			current_states[new_ms->mix_id - low_id] = new_ms;
6331 		    }
6332 		}
6333 	    }
6334 	}
6335       if (current_states) free(current_states);
6336     }
6337 }
6338 
6339 
ripple_mixes(chan_info * cp,mus_long_t beg,mus_long_t change)6340 static void ripple_mixes(chan_info *cp, mus_long_t beg, mus_long_t change)
6341 {
6342   ripple_mixes_1(cp, beg, 0, change, 1.0);
6343 }
6344 
6345 
ripple_mixes_with_scale(chan_info * cp,mus_long_t beg,mus_long_t len,mus_float_t scl)6346 static void ripple_mixes_with_scale(chan_info *cp, mus_long_t beg, mus_long_t len, mus_float_t scl)
6347 {
6348   ripple_mixes_1(cp, beg, len, 0, scl);
6349 }
6350 
6351 
make_virtual_mix_reader(chan_info * cp,mus_long_t beg,mus_long_t len,int index,mus_float_t scl,read_direction_t direction)6352 snd_fd *make_virtual_mix_reader(chan_info *cp, mus_long_t beg, mus_long_t len, int index, mus_float_t scl, read_direction_t direction)
6353 {
6354   snd_fd *sf;
6355   snd_data *first_snd;
6356   mus_long_t ind0, ind1, indx;
6357 
6358   sf = (snd_fd *)calloc(1, sizeof(snd_fd));
6359 
6360   sf->freed = false;
6361   sf->region = INVALID_MIX_ID;
6362   sf->type = MIX_READER;
6363   sf->initial_samp = beg;
6364   sf->cp = cp;
6365   sf->direction = direction;
6366   sf->current_state = initial_ed_list(0, len - 1);  /* need size field here to signal eof -- GC'd in free_reader_mixes */
6367   sf->cb = FRAGMENT(sf->current_state, 0);
6368   sf->edit_ctr = 0;
6369   first_snd = cp->sounds[index];
6370   sf->frag_pos = 0;
6371 
6372   ind0 = 0;
6373   indx = beg;
6374   ind1 = len - 1; /* ind1 (LOCAL_END...) is a sample number, not a length */
6375   sf->fscaler = scl;
6376 
6377   if ((scl == 1.0) &&
6378       (sf->fscaler == 1.0))
6379     {
6380       sf->runf = next_sample_value_unscaled;
6381       sf->rev_runf = previous_sample_value_unscaled;
6382     }
6383   else
6384     {
6385       if (scl == 0.0)
6386 	{
6387 	  sf->runf = next_zero;
6388 	  sf->rev_runf = previous_zero;
6389 	}
6390       else
6391 	{
6392 	  sf->runf = next_sample_value;
6393 	  sf->rev_runf = previous_sample_value;
6394 	}
6395     }
6396 
6397   if (direction == READ_BACKWARD)
6398     swap_readers(sf);
6399 
6400   if (first_snd->type == SND_DATA_FILE)
6401     {
6402 
6403       if (first_snd->inuse)
6404 	{
6405 	  first_snd = copy_snd_data(first_snd, beg, FILE_BUFFER_SIZE);
6406 	  if (!first_snd)
6407 	    return(cancel_reader(sf));
6408 	}
6409 
6410       first_snd->inuse = true;
6411       sf->current_sound = first_snd;
6412       sf->data = first_snd->buffered_data;
6413       if (direction == READ_FORWARD)
6414 	file_buffers_forward(ind0, ind1, indx, sf, first_snd);
6415       else file_buffers_back(ind0, ind1, indx, sf, first_snd);
6416     }
6417   else
6418     {
6419       sf->current_sound = NULL;
6420       sf->data = first_snd->buffered_data;
6421       sf->first = ind0;
6422       sf->last = ind1;
6423       sf->loc = indx;
6424     }
6425   return(sf);
6426 }
6427 
6428 
begin_mix_op(chan_info * cp,mus_long_t old_beg,mus_long_t old_len,mus_long_t new_beg,mus_long_t new_len,int edpos,const char * caller)6429 bool begin_mix_op(chan_info *cp, mus_long_t old_beg, mus_long_t old_len, mus_long_t new_beg, mus_long_t new_len, int edpos, const char *caller)
6430 {
6431   int i;
6432   ed_list *new_ed, *old_ed, *temp_ed;
6433   mus_long_t new_samples;
6434 
6435   old_ed = cp->edits[edpos];
6436   if (!(prepare_edit_list(cp, edpos, caller)))
6437     return(false);
6438 
6439   new_samples = new_beg + new_len;
6440 
6441   if (new_samples > old_ed->samples)
6442     {
6443       ed_fragment *cb;
6444       mus_long_t old_pos;
6445       cb = make_ed_fragment();
6446 
6447       old_pos = FRAGMENT_GLOBAL_POSITION(old_ed, old_ed->size - 1);
6448       temp_ed = make_ed_list(old_ed->size + 1);
6449       for (i = 0; i < old_ed->size; i++)
6450 	copy_ed_fragment(FRAGMENT(temp_ed, i), FRAGMENT(old_ed, i));
6451       FRAGMENT_GLOBAL_POSITION(temp_ed, old_ed->size - 1) = new_samples + 1;
6452 
6453       if (FRAGMENT(temp_ed, old_ed->size)) free_ed_fragment(FRAGMENT(temp_ed, old_ed->size));
6454       FRAGMENT(temp_ed, old_ed->size) = FRAGMENT(temp_ed, old_ed->size - 1);
6455       FRAGMENT(temp_ed, old_ed->size - 1) = cb;
6456       ED_SOUND(cb) = EDIT_LIST_ZERO_MARK;
6457       ED_SCALER(cb) = 0.0;
6458       ED_TYPE(cb) = ED_ZERO;
6459       ED_GLOBAL_POSITION(cb) = old_pos;
6460       ED_LOCAL_POSITION(cb) = 0;
6461       ED_LOCAL_END(cb) = new_samples - old_ed->samples;
6462     }
6463   else temp_ed = old_ed;
6464 
6465   if ((new_beg == old_beg) &&
6466       (new_len == old_len))
6467     {
6468       if (temp_ed != old_ed)
6469 	new_ed = temp_ed;
6470       else
6471 	{
6472 	  new_ed = make_ed_list(temp_ed->size);
6473 	  for (i = 0; i < temp_ed->size; i++)
6474 	    copy_ed_fragment(FRAGMENT(new_ed, i), FRAGMENT(temp_ed, i));
6475 	}
6476       new_ed->beg = new_beg;
6477       new_ed->len = new_len;
6478     }
6479   else
6480     {
6481       mus_long_t old_end, new_end;
6482       new_ed = copy_and_split_list(new_beg, new_len, temp_ed);
6483       if (temp_ed != old_ed)
6484 	{
6485 	  for (i = 0; i < temp_ed->allocated_size; i++)
6486 	    free_ed_fragment(FRAGMENT(temp_ed, i));
6487 	  free(FRAGMENTS(temp_ed));
6488 	  free(temp_ed);
6489 	}
6490       old_end = old_beg + old_len;
6491       new_end = new_beg + new_len;
6492       if (old_beg < new_beg)
6493 	new_ed->beg = old_beg;
6494       else new_ed->beg = new_beg;
6495       if (old_end > new_end)
6496 	new_ed->len = old_end - new_ed->beg + 1;
6497       else new_ed->len = new_end - new_ed->beg + 1;
6498     }
6499 
6500   if (new_samples > old_ed->samples)
6501     new_ed->samples = new_samples;
6502   else new_ed->samples = old_ed->samples;
6503 
6504   new_ed->cursor = old_ed->cursor;
6505   new_ed->edit_type = CHANGE_MIX_EDIT;
6506   new_ed->sound_location = old_ed->sound_location;
6507   new_ed->origin = mus_strdup(caller);
6508   new_ed->edpos = edpos;
6509   new_ed->selection_beg = old_ed->selection_beg;
6510   new_ed->selection_end = old_ed->selection_end;
6511   cp->edits[cp->edit_ctr] = new_ed;
6512   ripple_all(cp, 0, 0); /* 0,0 -> copy current mix (and mark) lists */
6513 
6514   return(true);
6515 }
6516 
6517 
check_splice_at(ed_list * new_ed,mus_long_t beg,int start)6518 static int check_splice_at(ed_list *new_ed, mus_long_t beg, int start)
6519 {
6520   int i;
6521   for (i = start; i < new_ed->size; i++)
6522     {
6523       if ((FRAGMENT_GLOBAL_POSITION(new_ed, i) == beg) &&
6524 	  ((FRAGMENT_TYPE(new_ed, i) == ED_SIMPLE) || (FRAGMENT_TYPE(new_ed, i) == ED_ZERO)) &&
6525 	  (FRAGMENT_TYPE(new_ed, i - 1) == FRAGMENT_TYPE(new_ed, i)) &&
6526 	  (FRAGMENT_SOUND(new_ed, i) == FRAGMENT_SOUND(new_ed, i - 1)) &&
6527 	  (FRAGMENT_SCALER(new_ed, i) == FRAGMENT_SCALER(new_ed, i -1)) &&
6528 	  (FRAGMENT_LOCAL_END(new_ed, i - 1) == FRAGMENT_LOCAL_POSITION(new_ed, i) - 1))
6529 	{
6530 	  int k;
6531 	  FRAGMENT_LOCAL_END(new_ed, i - 1) = FRAGMENT_LOCAL_END(new_ed, i);
6532 	  free_ed_fragment(FRAGMENT(new_ed, i));
6533 	  for (k = i + 1; k < new_ed->size; k++)
6534 	    FRAGMENT(new_ed, k - 1) = FRAGMENT(new_ed, k);
6535 	  FRAGMENT(new_ed, new_ed->size - 1) = NULL;
6536 	  new_ed->size--;
6537 	  return(i);
6538 	}
6539     }
6540   return(0);
6541 }
6542 
6543 
end_mix_op(chan_info * cp,mus_long_t old_beg,mus_long_t old_len)6544 void end_mix_op(chan_info *cp, mus_long_t old_beg, mus_long_t old_len)
6545 {
6546   /* if beg != 0, try to remove old splice points */
6547   if (old_beg > 0)
6548     {
6549       int start_loc;
6550       ed_list *new_ed;
6551       new_ed = cp->edits[cp->edit_ctr];
6552       start_loc = check_splice_at(new_ed, old_beg, 1);
6553       if (old_len > 0)
6554 	check_splice_at(new_ed, old_beg + old_len, start_loc);
6555     }
6556 
6557   if (cp->edits[cp->edit_ctr - 1]->samples != cp->edits[cp->edit_ctr]->samples)
6558     reflect_sample_change_in_axis(cp);
6559   reflect_mix_change(ANY_MIX_ID);
6560 }
6561 
6562 
6563 
6564 
6565 
6566 /* ----------------------- Xen connection -------------------------------- */
6567 
g_display_edits(Xen snd,Xen chn,Xen edpos)6568 static Xen g_display_edits(Xen snd, Xen chn, Xen edpos)
6569 {
6570   #define H_display_edits "(" S_display_edits " :optional snd chn edpos): current edit tree"
6571   FILE *tmp = NULL;
6572   char *buf, *name;
6573   chan_info *cp;
6574   int fd, pos = AT_CURRENT_EDIT_POSITION;
6575   mus_long_t len;
6576   Xen res;
6577   ssize_t bytes;
6578 
6579   Snd_assert_channel(S_display_edits, snd, chn, 1);
6580   cp = get_cp(snd, chn, S_display_edits);
6581   if (!cp) return(Xen_false);
6582 
6583   if (Xen_is_integer(edpos))
6584     {
6585       pos = Xen_integer_to_C_int(edpos);
6586       if (pos == AT_CURRENT_EDIT_POSITION)
6587 	pos = cp->edit_ctr;
6588       if ((pos < 0) || (pos >= cp->edit_size) || (!(cp->edits[pos])))
6589 	Xen_error(Xen_make_error_type("no-such-edit"),
6590 		  Xen_list_2(C_string_to_Xen_string(S_display_edits ": no such edit: ~A"),
6591 			     edpos));
6592     }
6593   name = snd_tempnam();
6594   tmp = FOPEN(name, "w");
6595   if (tmp)
6596     {
6597       if (pos != AT_CURRENT_EDIT_POSITION)
6598 	display_ed_list(cp, tmp, pos, cp->edits[pos]);
6599       else display_edits(cp, tmp);
6600       snd_fclose(tmp, name);
6601     }
6602   else Xen_error(Xen_make_error_type("cannot-save"),
6603 		 Xen_list_3(C_string_to_Xen_string(S_display_edits ": can't save ~S, ~A"),
6604 			    C_string_to_Xen_string(name),
6605 			    C_string_to_Xen_string(snd_io_strerror())));
6606   fd = mus_file_open_read(name);
6607   len = lseek(fd, 0L, SEEK_END);
6608   buf = (char *)calloc(len + 1, sizeof(char));
6609   lseek(fd, 0L, SEEK_SET);
6610   bytes = read(fd, buf, len);
6611   snd_close(fd, name);
6612   snd_remove(name, IGNORE_CACHE);
6613   if (name) free(name);
6614   if (bytes != 0)
6615     res = C_string_to_Xen_string(buf);
6616   else res = C_string_to_Xen_symbol("read-error");
6617   free(buf);
6618   return(res);
6619 }
6620 
6621 
g_edit_fragment(Xen uctr,Xen snd,Xen chn)6622 static Xen g_edit_fragment(Xen uctr, Xen snd, Xen chn)
6623 {
6624   #define H_edit_fragment "(" S_edit_fragment " :optional (ctr " S_current_edit_position ") snd chn): edit history entry at ctr \
6625 associated with snd's channel chn; the returned value is a list (origin type start-sample samps)"
6626 
6627   chan_info *cp;
6628   int ctr;
6629   Snd_assert_channel(S_edit_fragment, snd, chn, 2);
6630   Xen_check_type(Xen_is_integer_or_unbound(uctr), uctr, 1, S_edit_fragment, "an integer");
6631   cp = get_cp(snd, chn, S_edit_fragment);
6632   if (!cp) return(Xen_false);
6633 
6634   ctr = (Xen_is_integer(uctr)) ? Xen_integer_to_C_int(uctr) : cp->edit_ctr;
6635   if ((ctr < cp->edit_size) &&
6636       (ctr >= 0))
6637     {
6638       ed_list *ed;
6639       ed = cp->edits[ctr];
6640       if (ed)
6641 	return(Xen_list_4(C_string_to_Xen_string(ed->origin),
6642 			  C_string_to_Xen_string(edit_names[(int)(ed->edit_type)]),
6643 			  C_llong_to_Xen_llong(ed->beg),
6644 			  C_llong_to_Xen_llong(ed->len)));
6645     }
6646   Xen_error(Xen_make_error_type("no-such-edit"),
6647 	    Xen_list_2(C_string_to_Xen_string(S_edit_fragment ": no such edit ~A"),
6648 		       uctr));
6649   return(uctr);
6650 }
6651 
6652 
g_edit_tree(Xen snd,Xen chn,Xen upos)6653 static Xen g_edit_tree(Xen snd, Xen chn, Xen upos)
6654 {
6655   #define H_edit_tree "(" S_edit_tree " :optional snd chn edpos): \
6656 the edit lists '((global-pos data-num local-pos local-end scaler rmp0 rmp1 type-name)...)"
6657   /* internal debugging (auto-test) aid -- return complete ed list at pos */
6658   int i, len, pos;
6659   chan_info *cp;
6660   ed_list *eds;
6661   Xen res = Xen_empty_list;
6662 
6663   Snd_assert_channel(S_edit_tree, snd, chn, 1);
6664   cp = get_cp(snd, chn, S_edit_tree);
6665   if (!cp) return(Xen_false);
6666   pos = to_c_edit_position(cp, upos, S_edit_tree, 3);
6667   eds = cp->edits[pos];
6668   len = eds->size; /* fragments in this list */
6669   for (i = len - 1; i >= 0; i--)
6670     {
6671       ed_fragment *ed;
6672       mus_float_t rbeg, rend;
6673       ed = FRAGMENT(eds, i);
6674       if ((ED_RAMPS(ed)) && (ED_RAMP_LIST_SIZE(ed) > 0))
6675 	{
6676 	  /* this is how it used to work -- kinda dumb -- also we need the op name, not the number */
6677 	  rbeg = ED_RAMP_START(ed, 0);
6678 	  rend = ED_RAMP_INCR(ed, 0);
6679 	}
6680       else
6681 	{
6682 	  rbeg = 0.0;
6683 	  rend = 0.0;
6684 	}
6685       res = Xen_cons(Xen_list_8(C_llong_to_Xen_llong(ED_GLOBAL_POSITION(ed)),
6686 				C_int_to_Xen_integer(ED_SOUND(ed)),
6687 				C_llong_to_Xen_llong(ED_LOCAL_POSITION(ed)),
6688 				C_llong_to_Xen_llong(ED_LOCAL_END(ed)),
6689 				C_double_to_Xen_real(ED_SCALER(ed)),
6690 				C_double_to_Xen_real(rbeg),
6691 				C_double_to_Xen_real(rend),
6692 				C_int_to_Xen_integer(ED_TYPE(ed))),
6693 		     res);
6694     }
6695   return(res);
6696 }
6697 
6698 
6699 #define S_edit_fragment_type_name "edit-fragment-type-name"
g_edit_fragment_type_name(Xen type)6700 static Xen g_edit_fragment_type_name(Xen type)
6701 {
6702   int typ;
6703   Xen_check_type(Xen_is_integer(type), type, 1, S_edit_fragment_type_name, "an int");
6704   typ = Xen_integer_to_C_int(type);
6705   if ((typ >= 0) && (typ < NUM_OPS))
6706     return(C_string_to_Xen_string(type_info[typ].name));
6707   return(Xen_false);
6708 }
6709 
6710 
6711 
6712 /* ---------------- samplers ---------------- */
6713 
6714 static Xen_object_type_t sf_tag;
6715 
is_sampler(Xen obj)6716 bool is_sampler(Xen obj) {return(Xen_c_object_is_type(obj, sf_tag));}
6717 
6718 #define is_any_sampler(Obj) ((is_sampler(Obj)) || (is_mix_sampler(Obj)))
6719 
6720 
xen_to_sampler(Xen obj)6721 snd_fd *xen_to_sampler(Xen obj) {if (is_sampler(obj)) return((snd_fd *)Xen_object_ref(obj)); else return(NULL);}
6722 
6723 #define Xen_to_C_sampler(obj) ((snd_fd *)Xen_object_ref(obj))
6724 
6725 
6726 #if HAVE_SCHEME
s7_sf_is_equal(s7_scheme * sc,s7_pointer args)6727 static s7_pointer s7_sf_is_equal(s7_scheme *sc, s7_pointer args)
6728 {
6729   return(s7_make_boolean(sc, s7_car(args) == s7_cadr(args)));
6730 }
6731 
length_sf(s7_scheme * sc,s7_pointer args)6732 static s7_pointer length_sf(s7_scheme *sc, s7_pointer args)
6733 {
6734   snd_fd *fd;
6735   fd = (snd_fd *)s7_c_object_value(s7_car(args));
6736   return(s7_make_integer(sc, current_samples(fd->cp)));
6737 }
6738 #endif
6739 
6740 
sampler_to_string(snd_fd * fd)6741 char *sampler_to_string(snd_fd *fd)
6742 {
6743   char *desc;
6744   chan_info *cp;
6745 #if HAVE_SCHEME
6746   desc = (char *)calloc(PRINT_BUFFER_SIZE, sizeof(char));
6747 #else
6748   desc = (char *)calloc(PRINT_BUFFER_SIZE, sizeof(char));
6749 #endif
6750   if (!fd)
6751     snprintf(desc, PRINT_BUFFER_SIZE, "#<sampler: null>");
6752   else
6753     {
6754       const char *name = NULL;
6755       cp = fd->cp;
6756       if ((fd->local_sp) && (fd->local_sp->hdr))
6757 	name = (const char *)(((fd->local_sp)->hdr)->name);
6758       else
6759 	{
6760 	  if ((cp) && (cp->sound) && (cp->active >= CHANNEL_HAS_EDIT_LIST) && (!(fd->at_eof)))
6761 	    {
6762 	      if (fd->type == SAMPLER)
6763 		{
6764 		  name = cp->sound->short_filename;
6765 		  if (!name)
6766 		    switch (cp->sound->inuse)
6767 		      {
6768 		      case SOUND_IDLE:    name = "idle source";      break;
6769 		      case SOUND_NORMAL:  name = "unknown source";   break;
6770 		      case SOUND_WRAPPER: name = "wrapped source";   break;
6771 		      case SOUND_REGION:  name = "region as source"; break;
6772 		      case SOUND_READER:  name = "readable source";  break;
6773 		      }
6774 		}
6775 	      else name = "region as source";
6776 	    }
6777 	}
6778       if (!name) name = "unknown source";
6779       if (fd->at_eof)
6780 	snprintf(desc, PRINT_BUFFER_SIZE, "#<sampler: %s at eof or freed>",
6781 		     name);
6782       else
6783 	{
6784 	  if (cp)
6785 	    snprintf(desc, PRINT_BUFFER_SIZE, "#<sampler: %s[%d: %d] from %" print_mus_long ", at %" print_mus_long ", %s>",
6786 			 name, cp->chan, fd->edit_ctr, fd->initial_samp, current_location(fd),
6787 			 (fd->direction == READ_BACKWARD) ? "backward" : "forward");
6788 	  else snprintf(desc, PRINT_BUFFER_SIZE, "#<sampler: %s from %" print_mus_long ", at %" print_mus_long ", %s>",
6789 			    name, fd->initial_samp, current_location(fd),
6790 			    (fd->direction == READ_BACKWARD) ? "backward" : "forward");
6791 	}
6792     }
6793   return(desc);
6794 }
6795 
6796 #if HAVE_FORTH || HAVE_RUBY
Xen_wrap_print(snd_fd,print_sf,sampler_to_string)6797 Xen_wrap_print(snd_fd, print_sf, sampler_to_string)
6798 #endif
6799 
6800 #if HAVE_SCHEME
6801 static s7_pointer g_sampler_to_string(s7_scheme *sc, s7_pointer args)
6802 {
6803   char *str;
6804   s7_pointer result;
6805   str = sampler_to_string(Xen_to_C_sampler(s7_car(args)));
6806   result = s7_make_string(sc, str);
6807   if (str) free(str);
6808   return(result);
6809 }
6810 #endif
6811 
6812 /* make-sampler can refer to any edit of any sound, user can subsequently
6813  *   either clobber that edit (undo, new edit), or close the sound, but forget
6814  *   that the reader is now invalid.  So, we keep a list of these and unconnect
6815  *   them by hand when an edit is pruned or a sound is closed.
6816  *
6817  * channel|sound-properties are ok in this regard because the variable stays in
6818  *   xen and is merely cleared, not freed at the C level.
6819  */
6820 
list_reader(snd_fd * fd)6821 static void list_reader(snd_fd *fd)
6822 {
6823   ed_list *ed;
6824   ed = fd->current_state;
6825   if (ed)
6826     {
6827       int loc = -1;
6828       sf_info *lst = NULL;
6829       if (!ed->readers)
6830 	{
6831 	  ed->readers = (void *)calloc(1, sizeof(sf_info));
6832 	  lst = (sf_info *)(ed->readers);
6833 	  lst->size = 2;
6834 	  lst->rds = (snd_fd **)calloc(2, sizeof(snd_fd *));
6835 	  loc = 0;
6836 	}
6837       else
6838 	{
6839 	  int i;
6840 	  lst = (sf_info *)(ed->readers);
6841 	  for (i = 0; i < lst->size; i++)
6842 	    if (!(lst->rds[i]))
6843 	      {
6844 		loc = i;
6845 		break;
6846 	      }
6847 	  if (loc == -1)
6848 	    {
6849 	      loc = lst->size;
6850 	      lst->size *= 2;
6851 	      lst->rds = (snd_fd **)realloc(lst->rds, lst->size * sizeof(snd_fd *));
6852 	      for (i = loc; i < lst->size; i++)
6853 		lst->rds[i] = NULL;
6854 	    }
6855 	}
6856       lst->rds[loc] = fd;
6857     }
6858   else fprintf(stderr, "can't list reader?");
6859 }
6860 
6861 
unlist_reader(snd_fd * fd)6862 static void unlist_reader(snd_fd *fd)
6863 {
6864   if ((fd) &&
6865       (!(fd->freed)) &&
6866 #if 0
6867       (fd->cp) &&
6868       (fd->cp->active >= CHANNEL_HAS_EDIT_LIST) &&
6869 #endif
6870       (fd->current_state) &&
6871       (fd->current_state->readers))
6872     {
6873       int i;
6874       ed_list *ed;
6875       sf_info *lst;
6876       ed = fd->current_state;
6877       lst = (sf_info *)(ed->readers);
6878       for (i = 0; i < lst->size; i++)
6879 	if (fd == lst->rds[i])
6880 	  lst->rds[i] = NULL;
6881     }
6882   /* unlist and read can be called on fully freed xen-allocated readers accessed through mixing,
6883    *   but this happens only if the underlying sound has been closed -- nutty cases at the end
6884    *   of snd-test.scm -- Snd goes on, but valgrind complains about it.  I don't think this is
6885    *   a bug, since we're deliberately breaking the rules, so to speak.
6886    */
6887 }
6888 
6889 
sf_free(snd_fd * fd)6890 static void sf_free(snd_fd *fd)
6891 {
6892   if (fd)
6893     {
6894       snd_info *sp;
6895       /* changed to reflect g_free_sampler 29-Oct-00 */
6896       unlist_reader(fd);
6897       sp = fd->local_sp;
6898       fd->local_sp = NULL;
6899       free_snd_fd(fd);
6900       if (sp) completely_free_snd_info(sp);
6901     }
6902 }
6903 
6904 #if (!HAVE_SCHEME)
Xen_wrap_free(snd_fd,free_sf,sf_free)6905 Xen_wrap_free(snd_fd, free_sf, sf_free)
6906 /* sf_free is original, free_sf is wrapped form */
6907 #else
6908 static s7_pointer s7_sf_free(s7_scheme *sc, s7_pointer obj)
6909 {
6910   sf_free((snd_fd *)s7_c_object_value(obj));
6911   return(NULL);
6912 }
6913 #endif
6914 
6915 static Xen g_sampler_at_end(Xen obj)
6916 {
6917   #define H_sampler_at_end "(" S_is_sampler_at_end " obj): " PROC_TRUE " if sampler has reached the end of its data"
6918   Xen_check_type(is_any_sampler(obj), obj, 1, S_is_sampler_at_end, "a sampler (of any kind)");
6919 
6920   if (is_sampler(obj))
6921     {
6922       snd_fd *sf;
6923       sf = Xen_to_C_sampler(obj);
6924       return(C_bool_to_Xen_boolean(sf->at_eof));
6925     }
6926 
6927   if (is_mix_sampler(obj))
6928     return(g_mix_sampler_is_at_end(obj));
6929 
6930   return(Xen_false);
6931 }
6932 
6933 
6934 /* can sampler-position be settable?
6935  *   this requires that we find the fragment that holds the new position (as at the start of init_sample_read_any_with_bufsize 6892)
6936  *   set the fragment bounds (ind0, ind1), call file_buffers_forward|backward
6937  *   also check for reader_out_of_data complications, etc
6938  *   so, it's simpler and just as fast to require that the user make a new reader or use random access (channel->vct)
6939  *   (the only thing we avoid is choose_accessor)
6940  */
6941 
g_sampler_position(Xen obj)6942 static Xen g_sampler_position(Xen obj)
6943 {
6944   #define H_sampler_position "(" S_sampler_position " obj): current (sample-wise) location of sampler"
6945   Xen_check_type(is_any_sampler(obj), obj, 1, S_sampler_position, "a sampler (of any kind)");
6946 
6947   if (is_sampler(obj))
6948     {
6949       snd_fd *fd;
6950       fd = Xen_to_C_sampler(obj);
6951       if (fd->at_eof) return(Xen_integer_zero); /* -1? framples? */
6952       if ((fd->cp) &&
6953 	  (fd->cp->active >= CHANNEL_HAS_EDIT_LIST) &&
6954 	  (fd->cp->sound))
6955 	{
6956 	  if (fd->type == SAMPLER)
6957 	    return(C_llong_to_Xen_llong(current_location(fd)));
6958 	  return(C_llong_to_Xen_llong(region_current_location(fd)));
6959 	}
6960     }
6961   if (is_mix_sampler(obj))
6962     return(g_mix_sampler_position(obj));
6963 
6964   return(Xen_integer_zero);
6965 }
6966 
6967 
g_sampler_home(Xen obj)6968 static Xen g_sampler_home(Xen obj)
6969 {
6970   #define H_sampler_home "(" S_sampler_home " obj): (list sound-index chan-num) associated with a sound reader, or \
6971 if 'obj' is a mix-sampler, the id of underlying mix"
6972   Xen_check_type(is_any_sampler(obj), obj, 1, S_sampler_home, "a sampler (of any kind)");
6973 
6974   if (is_sampler(obj))
6975     {
6976       snd_fd *fd;
6977       fd = Xen_to_C_sampler(obj);
6978       if ((fd->cp) &&
6979 	  (fd->cp->active >= CHANNEL_HAS_EDIT_LIST) &&
6980 	  (fd->cp->sound))
6981 	{
6982 	  if (fd->type == SAMPLER)
6983 	    return(Xen_list_2(C_int_to_Xen_sound(fd->cp->sound->index),
6984 			      C_int_to_Xen_integer(fd->cp->chan)));
6985 
6986 	  return(Xen_list_2(C_int_to_Xen_region(fd->region),
6987 			    C_int_to_Xen_integer(fd->cp->chan)));
6988 	}
6989     }
6990   if (is_mix_sampler(obj))
6991     return(g_mix_sampler_home(obj));
6992 
6993   return(Xen_false);
6994 }
6995 
6996 
g_sampler_file_name(Xen obj)6997 Xen g_sampler_file_name(Xen obj)
6998 {
6999   if (is_sampler(obj))
7000     {
7001       snd_fd *fd;
7002       fd = Xen_to_C_sampler(obj);
7003       if ((fd->cp) &&
7004 	  (fd->cp->active >= CHANNEL_HAS_EDIT_LIST) &&
7005 	  (fd->cp->sound))
7006 	return(C_string_to_Xen_string(fd->cp->sound->filename));
7007       return(Xen_false);
7008     }
7009 
7010   return(C_string_to_Xen_string(mix_file_name(Xen_mix_to_C_int(obj))));
7011 }
7012 
7013 
g_c_make_sampler(snd_fd * fd)7014 Xen g_c_make_sampler(snd_fd *fd)
7015 {
7016   return(Xen_make_object(sf_tag, fd, 0, free_sf));
7017 }
7018 
7019 
g_make_region_sampler(Xen reg,Xen samp_n,Xen chn,Xen dir1)7020 static Xen g_make_region_sampler(Xen reg, Xen samp_n, Xen chn, Xen dir1)
7021 {
7022   #define H_make_region_sampler "(" S_make_region_sampler " reg :optional (start-samp 0) (chn 0) (dir 1)): \
7023 return a reader ready to access region's channel chn data starting at start-samp going in direction dir"
7024 
7025   snd_fd *fd = NULL;
7026   int reg_n, chn_n = 0;
7027   mus_long_t beg;
7028   int direction = 1;
7029 
7030   Xen_check_type(xen_is_region(reg), reg, 1, S_make_region_sampler, "a region");
7031   Xen_check_type(Xen_is_integer_or_unbound(samp_n), samp_n, 2, S_make_region_sampler, "an integer");
7032   Xen_check_type(Xen_is_integer_or_unbound(chn), chn, 3, S_make_region_sampler, "an integer");
7033   Xen_check_type(Xen_is_integer_boolean_or_unbound(dir1), dir1, 4, S_make_region_sampler, "an integer");
7034   reg_n = Xen_region_to_C_int(reg);
7035 
7036   if (!(region_ok(reg_n)))
7037     Xen_error(Xen_make_error_type("no-such-region"),
7038 	      Xen_list_2(C_string_to_Xen_string(S_make_region_sampler ": no such region: ~A"),
7039                          reg));
7040 
7041   if (Xen_is_integer(chn)) chn_n = Xen_integer_to_C_int(chn);
7042   if ((chn_n < 0) || (chn_n >= region_chans(reg_n)))
7043     return(snd_no_such_channel_error(S_make_region_sampler, Xen_list_1(reg), chn));
7044 
7045   beg = beg_to_sample(samp_n, S_make_region_sampler);
7046   if (Xen_is_integer(dir1)) direction = Xen_integer_to_C_int(dir1);
7047 
7048   if (direction == 1)
7049     fd = init_region_read(beg, reg_n, chn_n, READ_FORWARD);
7050   else
7051     {
7052       if (direction == -1)
7053 	fd = init_region_read(beg, reg_n, chn_n, READ_BACKWARD);
7054       else Xen_error(Xen_make_error_type("no-such-direction"),
7055 		     Xen_list_2(C_string_to_Xen_string(S_make_region_sampler ": bad direction: ~A"),
7056 				dir1));
7057     }
7058 
7059   if (fd)
7060     {
7061       fd->edit_ctr = -2 - reg_n; /* can't use fd->cp because deferred case is pointer to original (not copied) data */
7062                                  /* has to be less than -1 because that is the "delete all readers" sign on chan close */
7063       fd->region = reg_n;
7064       fd->type = REGION_READER;
7065       list_reader(fd);
7066       return(Xen_make_object(sf_tag, fd, 0, free_sf));
7067     }
7068 
7069   return(Xen_false);
7070 }
7071 
7072 
g_make_sampler(Xen samp_n,Xen snd,Xen chn,Xen dir1,Xen pos)7073 static Xen g_make_sampler(Xen samp_n, Xen snd, Xen chn, Xen dir1, Xen pos) /* "dir" confuses Mac OS-X Objective-C! */
7074 {
7075   #define H_make_sampler "(" S_make_sampler " :optional (start-samp 0) snd chn (dir 1) edpos): \
7076 return a reader ready to access snd's channel chn's data starting at start-samp, going in direction dir (1 = \
7077 forward, -1 = backward), reading the version of the data indicated by edpos which defaults to the current version. \
7078 snd can be a filename, a mix, a region, or a sound index number."
7079 
7080   snd_fd *fd = NULL;
7081   int edpos, direction = 1; /* in Scheme 1=forward, -1=backward */
7082   chan_info *cp;
7083   snd_info *loc_sp = NULL;
7084   mus_long_t beg;
7085 
7086   Xen_check_type(Xen_is_integer_or_unbound(samp_n), samp_n, 1, S_make_sampler, "an integer");
7087   Xen_check_type(Xen_is_integer_boolean_or_unbound(dir1), dir1, 4, S_make_sampler, "an integer");
7088 
7089   if (xen_is_mix(snd))
7090     return(g_make_mix_sampler(snd, samp_n));
7091 
7092   if (xen_is_region(snd))
7093     return(g_make_region_sampler(snd, samp_n, chn, dir1));
7094 
7095   if (Xen_is_string(snd))
7096     {
7097       const char *filename;
7098       int chan = 0;
7099       Xen_check_type(Xen_is_integer_boolean_or_unbound(chn), chn, 3, S_make_sampler, "an integer or boolean");
7100       filename = Xen_string_to_C_string(snd);
7101       if (mus_file_probe(filename))
7102 	loc_sp = make_sound_readable(filename, false);
7103       else return(snd_no_such_file_error(S_make_sampler, snd));
7104       if (Xen_is_integer(chn)) chan = Xen_integer_to_C_int(chn);
7105       if ((chan < 0) ||
7106 	  (chan >= (int)loc_sp->nchans))
7107 	{
7108 	  completely_free_snd_info(loc_sp);
7109 	  return(snd_no_such_channel_error(S_make_sampler, snd, chn));
7110 	}
7111       cp = loc_sp->chans[chan];
7112     }
7113   else
7114     {
7115       Snd_assert_channel(S_make_sampler, snd, chn, 2);
7116       cp = get_cp(snd, chn, S_make_sampler);
7117       if (!cp) return(Xen_false);
7118     }
7119 
7120   edpos = to_c_edit_position(cp, pos, S_make_sampler, 5);
7121   if (Xen_is_integer(dir1)) direction = Xen_integer_to_C_int(dir1);
7122   beg = beg_to_sample(samp_n, S_make_sampler);
7123 
7124   if (direction == 1)
7125     fd = init_sample_read_any(beg, cp, READ_FORWARD, edpos);
7126   else
7127     {
7128       if (direction == -1)
7129 	fd = init_sample_read_any(beg, cp, READ_BACKWARD, edpos);
7130       else Xen_error(Xen_make_error_type("no-such-direction"),
7131 		     Xen_list_2(C_string_to_Xen_string(S_make_sampler ": bad direction: ~A"),
7132 				dir1));
7133     }
7134 
7135   if (fd)
7136     {
7137       fd->local_sp = loc_sp;
7138       list_reader(fd);
7139       return(Xen_make_object(sf_tag, fd, 0, free_sf));
7140     }
7141 
7142   return(Xen_false);
7143 }
7144 
7145 
g_is_sampler(Xen obj)7146 static Xen g_is_sampler(Xen obj)
7147 {
7148   #define H_is_sampler "(" S_is_sampler " obj): " PROC_TRUE " if obj is a sound sampler."
7149 
7150   if (is_sampler(obj))
7151     {
7152       snd_fd *fd;
7153       fd = Xen_to_C_sampler(obj);
7154       return(C_bool_to_Xen_boolean((fd->type == SAMPLER) || (fd->type == REGION_READER)));
7155     }
7156   if (is_mix_sampler(obj))
7157     return(C_string_to_Xen_symbol("mix"));
7158 
7159   return(Xen_false);
7160 }
7161 
7162 
g_is_region_sampler(Xen obj)7163 static Xen g_is_region_sampler(Xen obj)
7164 {
7165   #define H_is_region_sampler "(" S_is_region_sampler " obj): " PROC_TRUE " if obj is a region sampler."
7166   if (is_sampler(obj))
7167     {
7168       snd_fd *fd;
7169       fd = Xen_to_C_sampler(obj);
7170       return(C_bool_to_Xen_boolean(fd->type == REGION_READER));
7171     }
7172   return(Xen_false);
7173 }
7174 
7175 
g_copy_sampler(Xen obj)7176 static Xen g_copy_sampler(Xen obj)
7177 {
7178   #define H_copy_sampler "(" S_copy_sampler " reader): return a copy of reader"
7179   Xen_check_type(is_any_sampler(obj), obj, 1, S_copy_sampler, "a sampler (of any kind)");
7180 
7181   if (is_sampler(obj))
7182     {
7183       snd_fd *fd;
7184       fd = Xen_to_C_sampler(obj);
7185       if ((fd->cp) &&
7186 	  (fd->cp->active >= CHANNEL_HAS_EDIT_LIST) &&
7187 	  (fd->cp->sound))
7188 	{
7189 	  if (fd->type == SAMPLER)
7190 	    return(g_make_sampler(C_llong_to_Xen_llong(current_location(fd)),
7191 				  C_int_to_Xen_sound(fd->cp->sound->index),
7192 				  C_int_to_Xen_integer(fd->cp->chan),
7193 				  C_int_to_Xen_integer((fd->direction == READ_FORWARD) ? 1 : -1), /* Scheme side is different from C side */
7194 				  C_int_to_Xen_integer(fd->edit_ctr)));
7195 
7196 	  return(g_make_region_sampler(C_int_to_Xen_region(fd->region),
7197 				       C_llong_to_Xen_llong(region_current_location(fd)),
7198 				       C_int_to_Xen_integer(fd->cp->chan),
7199 				       C_int_to_Xen_integer((fd->direction == READ_FORWARD) ? 1 : -1)));
7200 	}
7201       return(Xen_false);
7202     }
7203   if (is_mix_sampler(obj))
7204     return(g_copy_mix_sampler(obj));
7205 
7206   return(Xen_false);
7207 }
7208 
7209 
g_next_sample(Xen obj)7210 static Xen g_next_sample(Xen obj)
7211 {
7212   #define H_next_sample "(" S_next_sample " reader): next sample from reader"
7213 
7214   if (is_sampler(obj))
7215     return(C_double_to_Xen_real(protected_next_sample((snd_fd *)Xen_object_ref(obj))));
7216   if (is_mix_sampler(obj))
7217     return(C_double_to_Xen_real(protected_next_sample(xen_mix_to_snd_fd(obj))));
7218 
7219   Xen_check_type(false, obj, 1, S_next_sample, "a sampler");
7220   return(C_double_to_Xen_real(0.0));
7221 }
7222 
7223 
7224 mus_float_t read_sample_with_direction(void *p, int dir);
read_sample_with_direction(void * p,int dir)7225 mus_float_t read_sample_with_direction(void *p, int dir)
7226 {
7227   if (dir > 0)
7228     return(protected_next_sample((snd_fd *)p));
7229   return(protected_previous_sample((snd_fd *)p));
7230 }
7231 
7232 
g_read_sample(Xen obj)7233 static Xen g_read_sample(Xen obj)
7234 {
7235   #define H_read_sample "(" S_read_sample " reader): read sample from reader"
7236   if (is_sampler(obj))
7237     return(C_double_to_Xen_real(read_sample((snd_fd *)Xen_object_ref(obj))));
7238   if (is_mix_sampler(obj))
7239     return(C_double_to_Xen_real(read_sample(xen_mix_to_snd_fd(obj))));
7240 
7241   Xen_check_type(false, obj, 1, S_read_sample, "a sampler");
7242   return(C_double_to_Xen_real(0.0));
7243 }
7244 
7245 #define S_read_sample_with_direction "read-sample-with-direction"
7246 
g_read_sample_with_direction(Xen obj,Xen dir)7247 static Xen g_read_sample_with_direction(Xen obj, Xen dir)
7248 {
7249   #define H_read_sample_with_direction "(" S_read_sample_with_direction " reader dir): read sample from reader following dir (next/previous choice)"
7250   Xen_check_type(Xen_is_integer(dir), dir, 1, S_read_sample_with_direction, "an integer");
7251   if (is_sampler(obj))
7252     return(C_double_to_Xen_real(read_sample_with_direction((void *)Xen_object_ref(obj), Xen_integer_to_C_int(dir))));
7253   if (is_mix_sampler(obj))
7254     return(C_double_to_Xen_real(read_sample_with_direction((void *)xen_mix_to_snd_fd(obj), Xen_integer_to_C_int(dir))));
7255 
7256   Xen_check_type(false, obj, 1, S_read_sample_with_direction, "a sampler");
7257   return(C_double_to_Xen_real(0.0));
7258 }
7259 
7260 
7261 #if HAVE_SCHEME
s7_read_sample(s7_scheme * sc,Xen args)7262 static Xen s7_read_sample(s7_scheme *sc, Xen args)
7263 {
7264   /* we can only get here if obj is already known to be a sampler */
7265   return(C_double_to_Xen_real(read_sample((snd_fd *)Xen_object_ref(s7_car(args)))));
7266 }
7267 #endif
7268 
7269 
g_previous_sample(Xen obj)7270 static Xen g_previous_sample(Xen obj)
7271 {
7272   #define H_previous_sample "(" S_previous_sample " reader): previous sample from reader"
7273   if (is_sampler(obj))
7274     return(C_double_to_Xen_real(protected_previous_sample((snd_fd *)Xen_object_ref(obj))));
7275   if (is_mix_sampler(obj))
7276     return(C_double_to_Xen_real(protected_previous_sample(xen_mix_to_snd_fd(obj))));
7277 
7278   Xen_check_type(false, obj, 1, S_previous_sample, "a sampler");
7279   return(C_double_to_Xen_real(0.0));
7280 }
7281 
7282 
g_free_sampler(Xen obj)7283 static Xen g_free_sampler(Xen obj)
7284 {
7285   #define H_free_sampler "(" S_free_sampler " reader): free a sampler (of any kind)"
7286   Xen_check_type(is_any_sampler(obj), obj, 1, S_free_sampler, "a sampler");
7287 
7288   if (is_sampler(obj))
7289     {
7290       snd_info *sp;
7291       snd_fd *fd;
7292       fd = Xen_to_C_sampler(obj);
7293       unlist_reader(fd);
7294       sp = fd->local_sp;
7295       fd->local_sp = NULL;
7296       free_snd_fd_almost(fd); /* this is different from sf_free! */
7297       if (sp) completely_free_snd_info(sp);
7298     }
7299   if (is_mix_sampler(obj))
7300     return(g_free_mix_sampler(obj));
7301 
7302   return(Xen_false);
7303 }
7304 
7305 
g_save_edit_history(Xen filename,Xen snd,Xen chn)7306 static Xen g_save_edit_history(Xen filename, Xen snd, Xen chn)
7307 {
7308   #define H_save_edit_history "(" S_save_edit_history " filename :optional snd chn): save snd channel's chn edit history in filename"
7309   FILE *fd;
7310   const char *name;
7311   char *mcf = NULL;
7312 
7313   Xen_check_type(Xen_is_string(filename), filename, 1, S_save_edit_history, "a string");
7314   Snd_assert_channel(S_save_edit_history, snd, chn, 2);
7315 
7316   name = Xen_string_to_C_string(filename);
7317   mcf = mus_expand_filename(name);
7318   if (!mcf) return(Xen_false);
7319 
7320   fd = FOPEN(mcf, "w");
7321   free(mcf);
7322 
7323   if (fd)
7324     {
7325       if ((Xen_is_integer(chn)) &&
7326 	  (Xen_is_integer(snd) || xen_is_sound(snd)))
7327 	{
7328 	  chan_info *cp;
7329 	  cp = get_cp(snd, chn, S_save_edit_history);
7330 	  if (!cp) return(Xen_false);
7331 	  edit_history_to_file(fd, cp, false);
7332 	}
7333       else
7334 	{
7335 	  int i;
7336 	  snd_info *sp;
7337 	  if (Xen_is_integer(snd) || xen_is_sound(snd))
7338 	    {
7339 	      sp = get_sp(snd);
7340 	      if (sp)
7341 		for (i = 0; i < (int)sp->nchans; i++)
7342 		  edit_history_to_file(fd, sp->chans[i], false);
7343 	    }
7344 	  else
7345 	    {
7346 
7347 	      for (i = 0; i < ss->max_sounds; i++)
7348 		{
7349 		  uint32_t j;
7350 		  sp = ss->sounds[i];
7351 		  if ((sp) && (sp->inuse == SOUND_NORMAL))
7352 		    for (j = 0; j < sp->nchans; j++)
7353 		      edit_history_to_file(fd, sp->chans[j], false);
7354 		}
7355 	    }
7356 	}
7357       snd_fclose(fd, name);
7358     }
7359   else
7360     {
7361       Xen_error(Xen_make_error_type("cannot-save"),
7362 		Xen_list_3(C_string_to_Xen_string(S_save_edit_history ": can't save ~S: ~A"),
7363 			   filename,
7364 			   C_string_to_Xen_string(snd_open_strerror())));
7365     }
7366   return(filename);
7367 }
7368 
7369 
g_undo(Xen ed_n,Xen snd,Xen chn_n)7370 static Xen g_undo(Xen ed_n, Xen snd, Xen chn_n) /* opt ed_n */
7371 {
7372   #define H_undo "(" S_undo " :optional (count 1) snd chn): undo 'count' edits in snd's channel chn"
7373   chan_info *cp;
7374   Xen_check_type(Xen_is_integer_or_unbound(ed_n), ed_n, 1, S_undo, "an integer");
7375   Snd_assert_channel(S_undo, snd, chn_n, 2);
7376   cp = get_cp(snd, chn_n, S_undo);
7377   if (!cp) return(Xen_false);
7378   if (Xen_is_integer(ed_n))
7379     {
7380       int num;
7381       num = Xen_integer_to_C_int(ed_n);
7382       if ((num != 0) && (num < 1000000000) && (num > -1000000000))
7383 	{
7384 	  if (undo_edit_with_sync(cp, num))
7385 	    return(C_int_to_Xen_integer(num));
7386 	}
7387       return(Xen_integer_zero);
7388     }
7389   if (undo_edit_with_sync(cp, 1))
7390     return(C_int_to_Xen_integer(1));
7391   return(Xen_integer_zero);
7392 }
7393 
7394 
g_redo(Xen ed_n,Xen snd,Xen chn_n)7395 static Xen g_redo(Xen ed_n, Xen snd, Xen chn_n) /* opt ed_n */
7396 {
7397   #define H_redo "(" S_redo " :optional (count 1) snd chn): redo 'count' edits in snd's channel chn"
7398   chan_info *cp;
7399   Xen_check_type(Xen_is_integer_or_unbound(ed_n), ed_n, 1, S_redo, "an integer");
7400   Snd_assert_channel(S_redo, snd, chn_n, 2);
7401   cp = get_cp(snd, chn_n, S_redo);
7402   if (!cp) return(Xen_false);
7403   if (Xen_is_integer(ed_n))
7404     {
7405       int num;
7406       num = Xen_integer_to_C_int(ed_n);
7407       if ((num != 0) && (num < 1000000000) && (num > -1000000000))
7408 	{
7409 	  if (redo_edit_with_sync(cp, num))
7410 	    return(C_int_to_Xen_integer(num));
7411 	}
7412       return(Xen_integer_zero);
7413     }
7414   if (redo_edit_with_sync(cp, 1))
7415     return(C_int_to_Xen_integer(1));
7416   return(Xen_integer_zero);
7417 }
7418 
7419 
7420 /* ---------------------------------------- AS-ONE-EDIT ---------------------------------------- */
7421 
7422 #define INITIAL_AS_ONE_EDIT_POSITIONS_SIZE 2
7423 
as_one_edit(chan_info * cp,int one_edit)7424 void as_one_edit(chan_info *cp, int one_edit)
7425 {
7426   /* it's not safe to back up during the as-one-edit function call because that function might refer back via the edpos args etc */
7427 
7428   bool need_backup;
7429   need_backup = (cp->edit_ctr > one_edit);      /* cp->edit_ctr will be changing, so save this */
7430 
7431   if (cp->edit_ctr >= one_edit)                 /* ">=" here because the origin needs to be set even if there were no extra edits */
7432     {
7433       if (ss->deferred_regions > 0)
7434 	sequester_deferred_regions(cp, one_edit - 1);
7435       while (cp->edit_ctr > one_edit)
7436 	backup_edit_list_1(cp, true); /* i.e. free all the fragments */
7437       if (need_backup) prune_edits(cp, cp->edit_ctr + 1);
7438     }
7439 }
7440 
7441 
init_as_one_edit(chan_info * cp)7442 static void init_as_one_edit(chan_info *cp)
7443 {
7444   if (cp->in_as_one_edit == 0)
7445     cp->previous_squelch_update = cp->squelch_update; /* preserve possible user setting across as-one-edit call */
7446   cp->squelch_update = true;
7447   if (cp->as_one_edit_positions_size == 0)
7448     {
7449       cp->as_one_edit_positions_size = INITIAL_AS_ONE_EDIT_POSITIONS_SIZE;
7450       cp->as_one_edit_positions = (int *)calloc(cp->as_one_edit_positions_size, sizeof(int));
7451     }
7452   else
7453     {
7454       if (cp->in_as_one_edit >= cp->as_one_edit_positions_size)
7455 	{
7456 	  cp->as_one_edit_positions_size += INITIAL_AS_ONE_EDIT_POSITIONS_SIZE;
7457 	  cp->as_one_edit_positions = (int *)realloc(cp->as_one_edit_positions, cp->as_one_edit_positions_size * sizeof(int));
7458 	}
7459     }
7460   cp->as_one_edit_positions[cp->in_as_one_edit] = cp->edit_ctr;
7461   cp->in_as_one_edit++;
7462 }
7463 
7464 
as_one_edit_set_origin(chan_info * cp,void * origin)7465 static void as_one_edit_set_origin(chan_info *cp, void *origin)
7466 {
7467   if (cp->as_one_edit_positions)
7468     {
7469       if ((cp->as_one_edit_positions[cp->in_as_one_edit] + 1) == cp->edit_ctr)
7470 	{
7471 	  ed_list *ed;
7472 	  ed = cp->edits[cp->edit_ctr];
7473 	  if (ed)
7474 	    {
7475 	      if (ed->origin) free(ed->origin);
7476 	      ed->origin = mus_strdup((char *)origin);
7477 	      reflect_edit_history_change(cp);
7478 	    }
7479 	}
7480     }
7481 }
7482 
7483 
finish_as_one_edit(chan_info * cp)7484 static void finish_as_one_edit(chan_info *cp)
7485 {
7486   /* if a sound was opened within as-one-edit, it will have 0 here and no array */
7487   if ((cp->in_as_one_edit > 0) &&
7488       (cp->as_one_edit_positions))
7489     {
7490       cp->in_as_one_edit--;
7491       if (cp->in_as_one_edit < 0)
7492 	cp->in_as_one_edit = 0;
7493       as_one_edit(cp, cp->as_one_edit_positions[cp->in_as_one_edit] + 1);
7494       if (cp->in_as_one_edit == 0)
7495 	{
7496 	  cp->squelch_update = cp->previous_squelch_update;
7497 	  if (!(cp->squelch_update)) clear_status_area(cp->sound);
7498 	  reflect_edit_history_change(cp);
7499 	  update_graph(cp);
7500 	}
7501     }
7502 }
7503 
7504 #if HAVE_SCHEME
7505 static s7_pointer edit_finish;
g_edit_finish(s7_scheme * sc,s7_pointer args)7506 static s7_pointer g_edit_finish(s7_scheme *sc, s7_pointer args)
7507 {
7508   for_each_normal_chan(finish_as_one_edit);
7509   return(args);
7510 }
7511 #endif
7512 
g_as_one_edit(Xen proc,Xen origin)7513 static Xen g_as_one_edit(Xen proc, Xen origin)
7514 {
7515   #define H_as_one_edit "(" S_as_one_edit " thunk :optional origin): evaluate thunk, collecting all edits into one from the edit history's point of view"
7516   Xen result;
7517   char *errmsg, *as_one_edit_origin = NULL;
7518 
7519   Xen_check_type((Xen_is_procedure(proc)), proc, 1, S_as_one_edit, "a procedure");
7520   Xen_check_type(Xen_is_string_or_unbound(origin), origin, 2, S_as_one_edit, "a string");
7521   errmsg = procedure_ok(proc, 0, S_as_one_edit, "edit", 1);
7522   if (errmsg)
7523     {
7524       Xen errstr;
7525       errstr = C_string_to_Xen_string(errmsg);
7526       free(errmsg);
7527       return(snd_bad_arity_error(S_as_one_edit, errstr, proc));
7528     }
7529 
7530   if (Xen_is_string(origin))
7531     as_one_edit_origin = mus_strdup(Xen_string_to_C_string(origin));
7532   else as_one_edit_origin = NULL;
7533 
7534   for_each_normal_chan(init_as_one_edit);
7535 
7536 #if HAVE_SCHEME
7537   result = s7_dynamic_wind(s7, xen_false, proc, edit_finish);
7538 #else
7539   result = Xen_unprotected_call_with_no_args(proc);
7540   for_each_normal_chan(finish_as_one_edit);
7541 #endif
7542 
7543   if (as_one_edit_origin)
7544     {
7545       for_each_normal_chan_with_void(as_one_edit_set_origin, (void *)as_one_edit_origin);
7546       free(as_one_edit_origin);
7547     }
7548   return(result);
7549 }
7550 
7551 
g_scale_channel(Xen scl,Xen beg,Xen num,Xen snd,Xen chn,Xen edpos)7552 static Xen g_scale_channel(Xen scl, Xen beg, Xen num, Xen snd, Xen chn, Xen edpos)
7553 {
7554   #define H_scale_channel "(" S_scale_channel " scaler :optional (beg 0) (dur len) snd chn edpos): \
7555 scale samples in the given sound/channel between beg and beg + num by scaler."
7556 
7557   mus_float_t scaler;
7558   chan_info *cp;
7559   mus_long_t samp;
7560   int pos;
7561 
7562   Xen_check_type(Xen_is_number(scl), scl, 1, S_scale_channel, "a number");
7563   Snd_assert_sample_type(S_scale_channel, beg, 2);
7564   Snd_assert_sample_type(S_scale_channel, num, 3);
7565   Snd_assert_sound(S_scale_channel, snd, 4);
7566 
7567   scaler = Xen_real_to_C_double(scl);
7568   samp = beg_to_sample(beg, S_scale_channel);
7569   cp = get_cp(snd, chn, S_scale_channel);
7570   if (!cp) return(Xen_false);
7571   pos = to_c_edit_position(cp, edpos, S_scale_channel, 6);
7572 
7573   scale_channel(cp, scaler, samp, dur_to_samples(num, samp, cp, pos, 3, S_scale_channel), pos, NOT_IN_AS_ONE_EDIT);
7574 
7575   return(scl);
7576 }
7577 
7578 
g_normalize_channel(Xen scl,Xen beg,Xen num,Xen snd,Xen chn,Xen edpos)7579 static Xen g_normalize_channel(Xen scl, Xen beg, Xen num, Xen snd, Xen chn, Xen edpos)
7580 {
7581   #define H_normalize_channel "(" S_normalize_channel " norm :optional (beg 0) (dur len) snd chn edpos): \
7582 scale samples in the given sound/channel between beg and beg + num to norm."
7583 
7584   mus_float_t norm, cur_max;
7585   chan_info *cp;
7586   mus_long_t samp, samps;
7587   int pos;
7588   char *origin = NULL;
7589 
7590   Xen_check_type(Xen_is_number(scl), scl, 1, S_normalize_channel, "a number");
7591   Snd_assert_sample_type(S_normalize_channel, beg, 2);
7592   Snd_assert_sample_type(S_normalize_channel, num, 3);
7593   Snd_assert_sound(S_normalize_channel, snd, 4);
7594 
7595   norm = Xen_real_to_C_double(scl);
7596   samp = beg_to_sample(beg, S_normalize_channel);
7597   cp = get_cp(snd, chn, S_normalize_channel);
7598   if (!cp) return(Xen_false);
7599   pos = to_c_edit_position(cp, edpos, S_normalize_channel, 6);
7600   samps = dur_to_samples(num, samp, cp, pos, 3, S_normalize_channel);
7601 
7602   /* in order to normalize the data, we need its current maxamp */
7603 #if HAVE_FORTH
7604   if ((samp == 0) && (samps == cp->edits[pos]->samples))
7605     {
7606       cur_max = channel_maxamp(cp, pos);
7607       origin = mus_format("%.3f 0 " PROC_FALSE " %s", norm, S_normalize_channel);
7608     }
7609   else
7610     {
7611       cur_max = channel_local_maxamp(cp, samp, samps, pos, NULL);
7612       origin = mus_format("%.3f %" print_mus_long PROC_SEP "%" print_mus_long " %s", norm, samp, samps, S_normalize_channel);
7613     }
7614 #else
7615   if ((samp == 0) && (samps == cp->edits[pos]->samples))
7616     {
7617       cur_max = channel_maxamp(cp, pos);
7618       origin = mus_format("%s" PROC_OPEN "%.3f" PROC_SEP "0" PROC_SEP PROC_FALSE, to_proc_name(S_normalize_channel), norm);
7619     }
7620   else
7621     {
7622       cur_max = channel_local_maxamp(cp, samp, samps, pos, NULL);
7623       origin = mus_format("%s" PROC_OPEN "%.3f" PROC_SEP "%" print_mus_long PROC_SEP "%" print_mus_long, to_proc_name(S_normalize_channel), norm, samp, samps);
7624     }
7625 #endif
7626 
7627   if (cur_max != 0.0)
7628     scale_channel_with_origin(cp, norm / cur_max, samp, samps, pos, NOT_IN_AS_ONE_EDIT, origin);
7629   if (origin) free(origin);
7630 
7631   return(scl);
7632 }
7633 
7634 
channel_maxamp_and_position(chan_info * cp,int edpos,mus_long_t * maxpos)7635 mus_float_t channel_maxamp_and_position(chan_info *cp, int edpos, mus_long_t *maxpos)
7636 {
7637   /* maxamp position is not tracked in peak env because it gloms up the code, and cannot easily be saved/restored in the peak env files */
7638   mus_float_t val;
7639   int pos;
7640   mus_long_t locpos;
7641 
7642   if (edpos == AT_CURRENT_EDIT_POSITION) pos = cp->edit_ctr; else pos = edpos;
7643 
7644   if ((peak_env_maxamp_ok(cp, pos)) && (!maxpos))
7645     return(peak_env_maxamp(cp, pos));
7646 
7647   val = ed_maxamp(cp, pos);
7648   locpos = ed_maxamp_position(cp, pos);
7649   if (maxpos) (*maxpos) = locpos;
7650   if ((val >= 0.0) &&                          /* defaults to -1.0! */
7651       ((!maxpos) || (locpos >= 0)))
7652     return(val);
7653 
7654   val = channel_local_maxamp(cp, 0, cp->edits[pos]->samples, pos, &locpos);
7655   set_ed_maxamp(cp, pos, val);
7656   set_ed_maxamp_position(cp, pos, locpos);
7657   if (maxpos) (*maxpos) = locpos;
7658 
7659   return(val);
7660 }
7661 
7662 
channel_maxamp(chan_info * cp,int edpos)7663 mus_float_t channel_maxamp(chan_info *cp, int edpos)
7664 {
7665   return(channel_maxamp_and_position(cp, edpos, NULL));
7666 }
7667 
7668 
channel_maxamp_position(chan_info * cp,int edpos)7669 mus_long_t channel_maxamp_position(chan_info *cp, int edpos)
7670 {
7671   mus_long_t maxpos = 0;
7672   channel_maxamp_and_position(cp, edpos, &maxpos);
7673   return(maxpos);
7674 }
7675 
7676 
channel_local_maxamp(chan_info * cp,mus_long_t beg,mus_long_t num,int edpos,mus_long_t * maxpos)7677 mus_float_t channel_local_maxamp(chan_info *cp, mus_long_t beg, mus_long_t num, int edpos, mus_long_t *maxpos)
7678 {
7679   snd_fd *sf;
7680   mus_float_t ymax, x;
7681   mus_float_t *d;
7682   mus_long_t i, k, lim8, kend, mpos;
7683 
7684   sf = init_sample_read_any_with_bufsize(beg, cp, READ_FORWARD, edpos, (num > MAX_BUFFER_SIZE) ? MAX_BUFFER_SIZE : num);
7685   if (!sf) return(0.0);
7686 
7687   ymax = 0.0;
7688   mpos = -1;
7689   i = 0;
7690 
7691   while (true)
7692     {
7693       mus_long_t dur, left, offset;
7694 
7695       dur = sf->last - sf->loc + 1; /* current fragment, we're always reading forward here */
7696       left = num - i;
7697       if (dur > left) dur = left;
7698       offset = i - sf->loc;
7699 
7700       if (sf->runf == next_sample_value_unscaled)
7701 	{
7702 	  kend = sf->loc + dur;
7703 	  lim8 = kend - 8;
7704 	  k = sf->loc;
7705 	  d = sf->data;
7706 
7707 	  while (k <= lim8)
7708 	    {
7709 	      x = fabs(d[k]); if (x > ymax) {ymax = x; mpos = k + offset;} k++;
7710 	      x = fabs(d[k]); if (x > ymax) {ymax = x; mpos = k + offset;} k++;
7711 	      x = fabs(d[k]); if (x > ymax) {ymax = x; mpos = k + offset;} k++;
7712 	      x = fabs(d[k]); if (x > ymax) {ymax = x; mpos = k + offset;} k++;
7713 	      x = fabs(d[k]); if (x > ymax) {ymax = x; mpos = k + offset;} k++;
7714 	      x = fabs(d[k]); if (x > ymax) {ymax = x; mpos = k + offset;} k++;
7715 	      x = fabs(d[k]); if (x > ymax) {ymax = x; mpos = k + offset;} k++;
7716 	      x = fabs(d[k]); if (x > ymax) {ymax = x; mpos = k + offset;} k++;
7717 	    }
7718 	  while (k < kend)
7719 	    {
7720 	      x = fabs(d[k]); if (x > ymax) {ymax = x; mpos = k + offset;} k++;
7721 	    }
7722 	  i += dur;
7723 	}
7724       else
7725 	{
7726 	  if (sf->runf == next_sample_value)
7727 	    {
7728 	      mus_float_t scl, lmax; /* provisional max -- we omit the scaling until the end */
7729 	      mus_long_t lpos;       /* provisional max position */
7730 
7731 	      scl = fabs(sf->fscaler);
7732 	      lpos = -1;
7733 	      lmax = 0.0;
7734 	      kend = sf->loc + dur;
7735 	      lim8 = kend - 8;
7736 	      d = sf->data;
7737 
7738 	      k = sf->loc;
7739 	      while (k <= lim8)
7740 		{
7741 		  x = fabs(d[k]); if (x > lmax) {lmax = x; lpos = k;} k++;
7742 		  x = fabs(d[k]); if (x > lmax) {lmax = x; lpos = k;} k++;
7743 		  x = fabs(d[k]); if (x > lmax) {lmax = x; lpos = k;} k++;
7744 		  x = fabs(d[k]); if (x > lmax) {lmax = x; lpos = k;} k++;
7745 		  x = fabs(d[k]); if (x > lmax) {lmax = x; lpos = k;} k++;
7746 		  x = fabs(d[k]); if (x > lmax) {lmax = x; lpos = k;} k++;
7747 		  x = fabs(d[k]); if (x > lmax) {lmax = x; lpos = k;} k++;
7748 		  x = fabs(d[k]); if (x > lmax) {lmax = x; lpos = k;} k++;
7749 		}
7750 	      while (k < kend)
7751 		{
7752 		  x = fabs(d[k]); if (x > lmax) {lmax = x; lpos = k;} k++;
7753 		}
7754 	      if (lmax * scl > ymax)
7755 		{
7756 		  ymax = lmax * scl;
7757 		  mpos = lpos + offset;
7758 		}
7759 	      i += dur;
7760 	    }
7761 	  else
7762 	    {
7763 	      if (sf->runf == next_ramp1)
7764 		{
7765 		  mus_float_t amp, incr;
7766 		  mus_long_t j;
7767 
7768 		  amp = READER_VAL(sf, 0);
7769 		  incr = READER_INCR(sf, 0);
7770 		  kend = sf->loc + dur;
7771 		  lim8 = kend - 4;
7772 		  d = sf->data;
7773 
7774 		  j = sf->loc;
7775 		  while (j <= lim8)
7776 		    {
7777 		      x = fabs(amp * d[j]); if (x > ymax) {ymax = x; mpos = j + offset;} amp += incr; j++;
7778 		      x = fabs(amp * d[j]); if (x > ymax) {ymax = x; mpos = j + offset;} amp += incr; j++;
7779 		      x = fabs(amp * d[j]); if (x > ymax) {ymax = x; mpos = j + offset;} amp += incr; j++;
7780 		      x = fabs(amp * d[j]); if (x > ymax) {ymax = x; mpos = j + offset;} amp += incr; j++;
7781 		    }
7782 		  while (j < kend)
7783 		    {
7784 		      x = fabs(amp * d[j]); if (x > ymax) {ymax = x; mpos = j + offset;} amp += incr; j++;
7785 		    }
7786 		  READER_VAL(sf, 0) = amp;
7787 		  i += dur;
7788 		}
7789 	      else
7790 		{
7791 		  if (sf->runf == end_sample_value)
7792 		    break;
7793 		  if (sf->runf == next_zero)
7794 		    i += dur;
7795 		  else
7796 		    {
7797 		      if (sf->runf == next_xramp1)
7798 			{
7799 			  mus_float_t off, scaler, xval, scl, incr;
7800 			  mus_long_t j;
7801 
7802 			  off = READER_XRAMP_OFFSET(sf, 0);
7803 			  scl = READER_SCALER(sf);
7804 			  scaler = READER_XRAMP_SCALER(sf, 0);
7805 			  xval = READER_XVAL(sf, 0);
7806 			  incr = READER_XINCR(sf, 0);
7807 
7808 			  d = sf->data;
7809 			  kend = sf->loc + dur;
7810 			  off *= scl;
7811 			  scaler *= scl;
7812 
7813 			  for (j = sf->loc; j < kend; j++)
7814 			    {
7815 			      x = fabs(d[j] * (off + scaler * xval)); if (x > ymax) {ymax = x; mpos = j + offset;}
7816 			      xval *= incr;
7817 			    }
7818 			  READER_XVAL(sf, 0) = xval;
7819 			  i += dur;
7820 			}
7821 		      else
7822 			{
7823 			  left = i + dur;
7824 			  for (; i < left; i++)
7825 			    {
7826 			      x = fabs(read_sample(sf)); if (x > ymax) {ymax = x; mpos = i;}
7827 			    }
7828 			}
7829 		    }
7830 		}
7831 	    }
7832 	}
7833       if (i >= num) break;
7834       next_sound_1(sf);
7835     }
7836 
7837   /* fprintf(stderr, "use %f %" print_mus_long "\n", ymax, mpos); */
7838   if ((edpos == 0) &&
7839       (beg == 0) &&
7840       (num == cp->edits[0]->samples)) /* was = ? 29-Dec-19 */
7841     mus_sound_channel_set_maxamp(cp->sound->filename, cp->chan, ymax, mpos);
7842 
7843   if (maxpos) (*maxpos) = mpos;
7844   free_snd_fd(sf);
7845 
7846   return(ymax);
7847 }
7848 
7849 
g_floats_to_samples(Xen obj,int * size,const char * caller,int position)7850 static mus_float_t *g_floats_to_samples(Xen obj, int *size, const char *caller, int position)
7851 {
7852   mus_float_t *vals = NULL;
7853   int i, num = 0;
7854 
7855   if (Xen_is_list(obj))
7856     {
7857       Xen lst;
7858       num = Xen_list_length(obj);
7859       if (num == 0) return(NULL);
7860       if (((*size) > 0) && (num > (*size)))
7861 	num = (*size);
7862       vals = (mus_float_t *)malloc(num * sizeof(mus_float_t));
7863       for (i = 0, lst = Xen_copy_arg(obj); i < num; i++, lst = Xen_cdr(lst))
7864 	vals[i] = (Xen_real_to_C_double(Xen_car(lst)));
7865     }
7866   else
7867     {
7868       if (Xen_is_vector(obj))
7869 	{
7870 	  num = Xen_vector_length(obj);
7871 	  if (num == 0) return(NULL);
7872 	  if (((*size) > 0) && (num > (*size)))
7873 	    num = (*size);
7874 	  vals = (mus_float_t *)malloc(num * sizeof(mus_float_t));
7875 	  for (i = 0; i < num; i++)
7876 	    vals[i] = (Xen_real_to_C_double(Xen_vector_ref(obj, i)));
7877 	}
7878       else
7879 	{
7880 	  /* this block probably can't happen anymore */
7881 	  if (mus_is_vct(obj))
7882 	    {
7883 	      vct *v;
7884 	      mus_float_t *vdata;
7885 	      v = Xen_to_vct(obj);
7886 	      vdata = mus_vct_data(v);
7887 	      num = mus_vct_length(v);
7888 	      if (((*size) > 0) && (num > (*size)))
7889 		num = (*size);
7890 	      vals = (mus_float_t *)malloc(num * sizeof(mus_float_t));
7891 	      for (i = 0; i < num; i++)
7892 		vals[i] = (vdata[i]);
7893 	    }
7894 	  else Xen_check_type(false, obj, position, caller, "a " S_vct ", vector, or list");
7895 	}
7896     }
7897   (*size) = num;
7898   return(vals);
7899 }
7900 
7901 
g_sample(Xen samp_n,Xen snd,Xen chn_n,Xen pos_n)7902 static Xen g_sample(Xen samp_n, Xen snd, Xen chn_n, Xen pos_n)
7903 {
7904   #define H_sample "(" S_sample " samp :optional snd chn edpos): \
7905 return sample samp in snd's channel chn (this is a slow access -- use samplers for speed)"
7906   mus_long_t beg;
7907   int pos;
7908   chan_info *cp;
7909 
7910   Xen_check_type(Xen_is_integer_or_unbound(samp_n), samp_n, 1, S_sample, "an integer");
7911 
7912   if (Xen_is_true(chn_n)) /* a convenience! */
7913     {
7914       Xen lst = Xen_empty_list;
7915       int i, loc;
7916       snd_info *sp;
7917 
7918       Snd_assert_sound(S_sample, snd, 1);
7919 
7920       sp = get_sp(snd);
7921       if (!sp) return(Xen_false);
7922 
7923       cp = any_selected_channel(sp);
7924       if (!cp) return(Xen_false);
7925 
7926       pos = to_c_edit_position(cp, pos_n, S_sample, 4);
7927       beg = beg_to_sample(samp_n, S_sample);
7928       loc = snd_protect(lst);
7929 
7930       for (i = 0; i < (int)sp->nchans; i++)
7931 	{
7932 	  if (pos > sp->chans[i]->edit_ctr)
7933 	    lst = Xen_cons(C_double_to_Xen_real(chn_sample(beg, sp->chans[i], sp->chans[i]->edit_ctr)), lst);
7934 	  else lst = Xen_cons(C_double_to_Xen_real(chn_sample(beg, sp->chans[i], pos)), lst);
7935 	}
7936 
7937       snd_unprotect_at(loc);
7938       return(lst);
7939     }
7940 
7941   Snd_assert_channel(S_sample, snd, chn_n, 2);
7942   cp = get_cp(snd, chn_n, S_sample);
7943   if (!cp) return(Xen_false);
7944 
7945   pos = to_c_edit_position(cp, pos_n, S_sample, 4);
7946   if (Xen_is_bound(samp_n))
7947     beg = beg_to_sample(samp_n, S_sample);
7948   else beg = cursor_sample(cp);
7949 
7950   return(C_double_to_Xen_real(chn_sample(beg, cp, pos)));
7951 }
7952 
7953 
g_set_sample(Xen samp_n,Xen val,Xen snd,Xen chn_n,Xen edpos)7954 static Xen g_set_sample(Xen samp_n, Xen val, Xen snd, Xen chn_n, Xen edpos)
7955 {
7956   /* each call consitutes a separate edit from the undo/redo point-of-view */
7957   chan_info *cp;
7958   int pos;
7959   char *origin;
7960   mus_long_t beg;
7961   mus_float_t fval;
7962   mus_float_t ival[1];
7963 
7964   Xen_check_type(Xen_is_integer_or_unbound(samp_n), samp_n, 1, S_set S_sample, "an integer");
7965   Xen_check_type(Xen_is_number(val), val, 2, S_set S_sample, "a number");
7966   Snd_assert_channel(S_set S_sample, snd, chn_n, 3);
7967   cp = get_cp(snd, chn_n, S_set S_sample);
7968   if (!cp) return(Xen_false);
7969   pos = to_c_edit_position(cp, edpos, S_set S_sample, 5);
7970   if (pos > cp->edit_ctr)
7971     Xen_error(Xen_make_error_type("no-such-edit"),
7972 	      Xen_list_2(C_string_to_Xen_string(S_set S_sample ": no such edit: ~A"),
7973 			 edpos));
7974   if (Xen_is_bound(samp_n))
7975     beg = beg_to_sample(samp_n, S_set S_sample);
7976   else beg = cursor_sample(cp);
7977 
7978   fval = Xen_real_to_C_double(val);
7979   if ((fval == 1.0) &&
7980       (mus_bytes_per_sample(((cp->sound)->hdr)->sample_type) == 2))
7981     fval = 32767.0 / 32768.0;
7982   ival[0] = fval;
7983 
7984 #if HAVE_FORTH
7985   origin = mus_format("%" print_mus_long " %.4f %s drop", beg, fval, "set-sample");
7986 #else
7987   origin = mus_format("%s" PROC_OPEN "%" print_mus_long PROC_SEP "%.4f", to_proc_name("set-sample"), beg, fval);
7988 #endif
7989   if (change_samples(beg, 1, ival, cp, origin, pos, fabs(fval)))
7990     update_graph(cp);
7991   free(origin);
7992   return(val);
7993 }
7994 
7995 
7996 #if HAVE_SCHEME
g_set_sample_reversed(s7_scheme * sc,s7_pointer args)7997 static Xen g_set_sample_reversed(s7_scheme *sc, s7_pointer args)
7998 {
7999   int len;
8000   len = Xen_list_length(args);
8001 
8002   if (len == 1)
8003     return(g_set_sample(Xen_undefined, Xen_list_ref(args, 0), Xen_undefined, Xen_undefined, Xen_undefined));
8004 
8005   if (len == 2)
8006     return(g_set_sample(Xen_list_ref(args, 0), Xen_list_ref(args, 1), Xen_undefined, Xen_undefined, Xen_undefined));
8007 
8008   if (len == 3)
8009     return(g_set_sample(Xen_list_ref(args, 0), Xen_list_ref(args, 2), Xen_list_ref(args, 1), Xen_undefined, Xen_undefined));
8010 
8011   if (len == 4)
8012     return(g_set_sample(Xen_list_ref(args, 0), Xen_list_ref(args, 3), Xen_list_ref(args, 1), Xen_list_ref(args, 2), Xen_undefined));
8013 
8014   return(g_set_sample(Xen_list_ref(args, 0), Xen_list_ref(args, 4), Xen_list_ref(args, 1), Xen_list_ref(args, 2), Xen_list_ref(args, 3)));
8015 }
8016 #endif
8017 
xen_to_file_delete_t(Xen auto_delete,const char * caller)8018 file_delete_t xen_to_file_delete_t(Xen auto_delete, const char *caller)
8019 {
8020   if (Xen_is_boolean(auto_delete))
8021     {
8022       if (Xen_boolean_to_C_bool(auto_delete))
8023 	return(DELETE_ME);
8024       else return(DONT_DELETE_ME);
8025     }
8026   else
8027     {
8028       if (Xen_is_integer(auto_delete))                            /* might be unbound */
8029 	{
8030 	  int val;
8031 	  val = Xen_integer_to_C_int(auto_delete);
8032 	  if ((val >= DONT_DELETE_ME) && (val <= MULTICHANNEL_DELETION_IF_FILE))
8033 	    return((file_delete_t)val);
8034 	  Xen_error(Xen_make_error_type("no-such-auto-delete-choice"),
8035 		    Xen_list_3(C_string_to_Xen_string("~A: no such auto-delete option: ~A"),
8036 			       C_string_to_Xen_string(caller),
8037 			       auto_delete));
8038 	}
8039     }
8040   return(DONT_DELETE_ME);
8041 }
8042 
8043 
g_set_samples_with_origin(Xen samp_0,Xen samps,Xen vect,Xen snd,Xen chn_n,Xen truncate,const char * edname,Xen infile_chan,Xen edpos,Xen auto_delete)8044 static Xen g_set_samples_with_origin(Xen samp_0, Xen samps, Xen vect, Xen snd, Xen chn_n, Xen truncate,
8045 				     const char *edname, Xen infile_chan, Xen edpos, Xen auto_delete)
8046 {
8047   #define H_set_samples "(set-" S_samples " start-samp samps data :optional snd chn truncate edname (infile-chan 0) edpos auto-delete): \
8048 set snd's channel chn's samples starting at start-samp for samps from data (a " S_vct ", vector, or string (filename)); \
8049 start-samp can be beyond current data end; if truncate is " PROC_TRUE " and start-samp is 0, the end of the file is set to match \
8050 the new data's end."
8051 
8052   chan_info *cp;
8053   mus_long_t len = 0, beg;
8054   bool override = false;
8055   int pos;
8056   const char *caller;
8057 
8058   if (edname)
8059     caller = edname;
8060   else caller = "set-samples";
8061 
8062   Snd_assert_sample_type(caller, samp_0, 1);
8063   Snd_assert_sample_type(caller, samps, 2);
8064   Snd_assert_channel(caller, snd, chn_n, 4);
8065   Xen_check_type(Xen_is_boolean_or_unbound(truncate), truncate, 6, caller, "a boolean");
8066 
8067   cp = get_cp(snd, chn_n, caller);
8068   if (!cp) return(Xen_false);
8069   Xen_check_type(Xen_is_integer_boolean_or_unbound(infile_chan), infile_chan, 8, caller, "an integer");
8070 
8071   pos = to_c_edit_position(cp, edpos, caller, 9);
8072   beg = beg_to_sample(samp_0, caller);
8073   len = dur_to_samples(samps, beg, cp, pos, 2, caller);
8074   if (len == 0) return(Xen_false);
8075   Xen_check_type(Xen_is_integer_boolean_or_unbound(auto_delete), auto_delete, 10, caller, "a boolean or an integer");
8076 
8077   override = Xen_is_true(truncate);
8078   if (Xen_is_string(vect))
8079     {
8080       int inchan = 0;
8081       file_delete_t delete_file = DONT_DELETE_ME;
8082       mus_long_t curlen;
8083       const char *fname;
8084 
8085       curlen = cp->edits[pos]->samples;
8086 
8087       fname = Xen_string_to_C_string(vect);
8088       if (!mus_file_probe(fname))
8089 	return(snd_no_such_file_error(caller, vect));
8090 
8091       if (Xen_is_integer(infile_chan)) inchan = Xen_integer_to_C_int(infile_chan);
8092       if ((inchan < 0) ||
8093 	  (inchan >= mus_sound_chans(fname)))
8094 	Xen_error(NO_SUCH_CHANNEL,
8095 		  Xen_list_5(C_string_to_Xen_string("~A: no such channel: ~A (~S has ~A chans)"),
8096 			     C_string_to_Xen_string(caller),
8097 			     infile_chan,
8098 			     vect,
8099 			     C_int_to_Xen_integer(mus_sound_chans(fname))));
8100 
8101       delete_file = xen_to_file_delete_t(auto_delete, caller);
8102       if ((beg == 0) &&
8103 	  ((len > curlen) || override))
8104 	file_override_samples(len, fname, cp, inchan, delete_file, caller);
8105       else file_change_samples(beg, len, fname, cp, inchan, delete_file, caller, pos);
8106     }
8107   else
8108     {
8109       if (mus_is_vct(vect))
8110 	{
8111 	  vct *v;
8112 	  v = Xen_to_vct(vect);
8113 	  if (len > mus_vct_length(v)) len = mus_vct_length(v);
8114 	  change_samples(beg, len, mus_vct_data(v), cp, caller, pos, -1.0);
8115 	}
8116       else
8117 	{
8118 	  mus_float_t *ivals;
8119 	  int ilen;
8120 	  ilen = (int)len;
8121 	  ivals = g_floats_to_samples(vect, &ilen, caller, 3);
8122 	  if (ivals)
8123 	    {
8124 	      change_samples(beg, (mus_long_t)ilen, ivals, cp, caller, pos, -1.0);
8125 	      free(ivals);
8126 	    }
8127 	}
8128     }
8129   update_graph(cp);
8130   return(vect);
8131 }
8132 
8133 
g_set_samples(Xen samp_0,Xen samps,Xen vect,Xen snd,Xen chn_n,Xen truncate,Xen edname,Xen infile_chan,Xen edpos,Xen auto_delete)8134 static Xen g_set_samples(Xen samp_0, Xen samps, Xen vect, Xen snd, Xen chn_n, Xen truncate, Xen edname, Xen infile_chan, Xen edpos, Xen auto_delete)
8135 {
8136   Xen_check_type(!Xen_is_bound(edname) || Xen_is_string(edname) || Xen_is_false(edname), edname, 7, "set-samples", "a string");
8137   return(g_set_samples_with_origin(samp_0, samps, vect, snd, chn_n, truncate,
8138 				   (char *)((Xen_is_string(edname)) ? Xen_string_to_C_string(edname) : "set-samples"),
8139 				   infile_chan, edpos, auto_delete));
8140 }
8141 
8142 
g_set_samples_any(Xen args)8143 static Xen g_set_samples_any(Xen args)
8144 {
8145   Xen arg;
8146   Xen samp_0 = Xen_undefined, samps = Xen_undefined, vect = Xen_undefined;
8147   Xen snd = Xen_undefined, chn_n = Xen_undefined, truncate = Xen_undefined;
8148   Xen edname = Xen_undefined, infile_chan = Xen_undefined, edpos = Xen_undefined, auto_delete = Xen_undefined;
8149   arg = args;
8150   if (!Xen_is_null(arg))
8151     {
8152       samp_0 = Xen_car(arg); arg = Xen_cdr(arg);
8153       if (!Xen_is_null(arg))
8154 	{
8155 	  samps = Xen_car(arg); arg = Xen_cdr(arg);
8156 	  if (!Xen_is_null(arg))
8157 	    {
8158 	      vect = Xen_car(arg); arg = Xen_cdr(arg);
8159 	      if (!Xen_is_null(arg))
8160 		{
8161 		  snd = Xen_car(arg); arg = Xen_cdr(arg);
8162 		  if (!Xen_is_null(arg))
8163 		    {
8164 		      chn_n = Xen_car(arg); arg = Xen_cdr(arg);
8165 		      if (!Xen_is_null(arg))
8166 			{
8167 			  truncate = Xen_car(arg); arg = Xen_cdr(arg);
8168 			  if (!Xen_is_null(arg))
8169 			    {
8170 			      edname = Xen_car(arg); arg = Xen_cdr(arg);
8171 			      if (!Xen_is_null(arg))
8172 				{
8173 				  infile_chan = Xen_car(arg); arg = Xen_cdr(arg);
8174 				  if (!Xen_is_null(arg))
8175 				    {
8176 				      edpos = Xen_car(arg); arg = Xen_cdr(arg);
8177 				      if (!Xen_is_null(arg))
8178 					auto_delete = Xen_car(arg);
8179 				    }}}}}}}}}
8180   return(g_set_samples(samp_0, samps, vect, snd, chn_n, truncate, edname, infile_chan, edpos, auto_delete));
8181 }
8182 
8183 
check_saved_temp_file(const char * type,Xen filename,Xen date_and_length)8184 void check_saved_temp_file(const char *type, Xen filename, Xen date_and_length)
8185 {
8186   const char *file;
8187 
8188   if (!Xen_is_list(date_and_length)) return; /* can this happen? */
8189 
8190   file = Xen_string_to_C_string(filename);
8191   if (mus_file_probe(file))
8192     {
8193       time_t old_time, new_time;
8194       mus_long_t old_bytes, new_bytes;
8195       old_time = (time_t)Xen_ulong_to_C_ulong(Xen_car(date_and_length));
8196       old_bytes = Xen_llong_to_C_llong(Xen_cadr(date_and_length));
8197       new_time = mus_sound_write_date(file);
8198       new_bytes = mus_sound_length(file);
8199       if ((new_time != old_time) || (new_bytes != old_bytes))
8200 	{
8201 	  char *buf = NULL;
8202 	  if (old_time != new_time)
8203 	    {
8204 	      if (old_bytes != new_bytes)
8205 		buf = mus_format("Saved %s temp file %s: original write date: %s, current: %s, original length: %" print_mus_long "bytes, current: %" print_mus_long,
8206 				 type, file,
8207 				 snd_strftime(STRFTIME_FORMAT, old_time),
8208 				 snd_strftime(STRFTIME_FORMAT, new_time),
8209 				 old_bytes, new_bytes);
8210 	      else
8211 		buf = mus_format("Saved %s temp file %s: original write date: %s, current: %s",
8212 				 type, file,
8213 				 snd_strftime(STRFTIME_FORMAT, old_time),
8214 				 snd_strftime(STRFTIME_FORMAT, new_time));
8215 	    }
8216 	  else buf = mus_format("Saved %s temp file %s: original length: %" print_mus_long "bytes, current: %" print_mus_long,
8217 				 type, file,
8218 				 old_bytes, new_bytes);
8219 	  snd_warning_without_format(buf);
8220 	  free(buf);
8221 	}
8222     }
8223 }
8224 
8225 
g_override_samples_with_origin(Xen filename,Xen samps,Xen snd,Xen chn_n,Xen origin,Xen date)8226 static Xen g_override_samples_with_origin(Xen filename, Xen samps, Xen snd, Xen chn_n, Xen origin, Xen date)
8227 {
8228   check_saved_temp_file("sound", filename, date);
8229   return(g_set_samples(Xen_integer_zero, samps, filename, snd, chn_n, Xen_true, origin, Xen_integer_zero, Xen_false, Xen_false));
8230 }
8231 
8232 
g_vct_to_channel(Xen v,Xen beg,Xen dur,Xen snd,Xen chn_n,Xen edpos,Xen origin)8233 static Xen g_vct_to_channel(Xen v, Xen beg, Xen dur, Xen snd, Xen chn_n, Xen edpos, Xen origin)
8234 {
8235   #define H_vct_to_channel "(" S_vct_to_channel " v :optional (beg 0) (dur len) snd chn edpos origin): \
8236 set snd's channel chn's samples starting at beg for dur samps from " S_vct " v"
8237   const char *caller;
8238   Xen_check_type(mus_is_vct(v), v, 1, S_vct_to_channel, "a " S_vct);
8239   Xen_check_type(Xen_is_string_or_unbound(origin), origin, 7, S_vct_to_channel, "a string");
8240   if (!Xen_is_bound(beg)) beg = Xen_integer_zero;
8241   if (!Xen_is_bound(dur))
8242     {
8243       vct *v1;
8244       v1 = Xen_to_vct(v);
8245       dur = C_int_to_Xen_integer(mus_vct_length(v1));
8246     }
8247   if (!Xen_is_bound(origin))
8248     caller = S_vct_to_channel;
8249   else caller = (const char *)Xen_string_to_C_string(origin);
8250   return(g_set_samples_with_origin(beg, dur, v, snd, chn_n, Xen_false, caller, Xen_false, edpos, Xen_undefined));
8251 }
8252 
8253 
samples_to_vct(mus_long_t beg,mus_long_t len,chan_info * cp,int pos,mus_float_t * buf,snd_fd * reader)8254 vct *samples_to_vct(mus_long_t beg, mus_long_t len, chan_info *cp, int pos, mus_float_t *buf, snd_fd *reader)
8255 {
8256   /* if reader, beg, cp, and pos are ignored */
8257   snd_fd *sf;
8258   vct *v = NULL;
8259   mus_float_t **d;
8260   mus_float_t *fvals;
8261 
8262   if (!buf)
8263     {
8264       v = mus_vct_make(len);
8265       fvals = mus_vct_data(v);
8266     }
8267   else
8268     {
8269       v = mus_vct_wrap(len, buf);
8270       fvals = buf;
8271     }
8272 
8273   if ((pos == 0) &&
8274       (beg + len <= cp->edits[0]->samples) &&
8275       (d = mus_sound_saved_data(cp->sound->filename)))
8276     {
8277       mus_float_t *dc;
8278       dc = d[cp->chan];
8279       memcpy((void *)fvals, (void *)(dc + beg), len * sizeof(mus_float_t));
8280       return(v);
8281     }
8282 
8283   if (!reader)
8284     sf = init_sample_read_any_with_bufsize(beg, cp, READ_FORWARD, pos, len);
8285   else sf = reader;
8286 
8287   if (sf)
8288     {
8289       mus_long_t i;
8290       i = 0;
8291       while (true)
8292 	{
8293 	  mus_long_t dur, left;
8294 	  dur = sf->last - sf->loc + 1; /* current fragment, we're always reading forward here */
8295 	  left = len - i;
8296 	  if (dur > left) dur = left;
8297 
8298 	  if (sf->runf == next_sample_value_unscaled)
8299 	    {
8300 	      mus_copy_floats(fvals + i, sf->data + sf->loc, dur);
8301 	      i += dur;
8302 	    }
8303 	  else
8304 	    {
8305 	      if (sf->runf == next_sample_value)
8306 		{
8307 		  mus_float_t scl;
8308 		  scl = sf->fscaler;
8309 		  mus_copy_floats(fvals + i, sf->data + sf->loc, dur);
8310 		  left = i + dur;
8311 		  for (; i < left; i++) fvals[i] *= scl;
8312 		}
8313 	      else
8314 		{
8315 		  if (sf->runf == next_ramp1)
8316 		    {
8317 		      mus_float_t amp, incr;
8318 		      mus_long_t j;
8319 		      amp = READER_VAL(sf, 0);
8320 		      incr = READER_INCR(sf, 0);
8321 		      left = i + dur;
8322 		      for (j = sf->loc; i < left; i++, j++)
8323 			{
8324 			  fvals[i] = amp * sf->data[j];
8325 			  amp += incr;
8326 			}
8327 		      READER_VAL(sf, 0) = amp;
8328 		    }
8329 		  else
8330 		    {
8331 		      if (sf->runf == end_sample_value)
8332 			break;
8333 		      if (sf->runf == next_zero)
8334 			i += dur;
8335 		      else
8336 			{
8337 			  if (sf->runf == next_xramp1)
8338 			    {
8339 			      mus_float_t offset, scaler, xval, scl, incr;
8340 			      mus_long_t j;
8341 			      offset = READER_XRAMP_OFFSET(sf, 0);
8342 			      scl = READER_SCALER(sf);
8343 			      scaler = READER_XRAMP_SCALER(sf, 0);
8344 			      xval = READER_XVAL(sf, 0);
8345 			      incr = READER_XINCR(sf, 0);
8346 
8347 			      left = i + dur;
8348 			      offset *= scl;
8349 			      scaler *= scl;
8350 			      for (j = sf->loc; i < left; i++, j++)
8351 				{
8352 				  fvals[i] = sf->data[j] * (offset + scaler * xval);
8353 				  xval *= incr;
8354 				}
8355 			      READER_XVAL(sf, 0) = xval;
8356 			    }
8357 			  else
8358 			    {
8359 			      left = i + dur;
8360 			      for (; i < left; i++) fvals[i] = read_sample(sf);
8361 			    }
8362 			}
8363 		    }
8364 		}
8365 	    }
8366 	  if (i >= len) break;
8367 	  next_sound_1(sf);
8368 	}
8369       if (!reader)
8370 	free_snd_fd(sf);
8371     }
8372   return(v);
8373 }
8374 
8375 
samples_to_vct_with_reader(mus_long_t len,mus_float_t * buf,snd_fd * reader)8376 vct *samples_to_vct_with_reader(mus_long_t len, mus_float_t *buf, snd_fd *reader)
8377 {
8378   return(samples_to_vct(0, len, NULL, -1, buf, reader));
8379 }
8380 
8381 
samples_to_vct_1(Xen samp_0,Xen samps,Xen snd,Xen chn_n,Xen edpos,const char * caller)8382 static Xen samples_to_vct_1(Xen samp_0, Xen samps, Xen snd, Xen chn_n, Xen edpos, const char *caller)
8383 {
8384   chan_info *cp;
8385   mus_long_t len, beg;
8386   int pos;
8387 
8388   Xen_check_type(Xen_is_integer_or_unbound(samp_0) || Xen_is_false(samp_0), samp_0, 1, caller, "an integer");
8389   Xen_check_type(Xen_is_integer_or_unbound(samps) || Xen_is_false(samps), samps, 2, caller, "an integer");
8390   Snd_assert_channel(caller, snd, chn_n, 3);
8391 
8392   cp = get_cp(snd, chn_n, caller);
8393   if (!cp) return(Xen_false);
8394 
8395   pos = to_c_edit_position(cp, edpos, caller, 6);
8396   beg = beg_to_sample(samp_0, caller);
8397   len = dur_to_samples(samps, beg, cp, pos, 2, caller);
8398   if (len == 0) return(Xen_false); /* empty file (channel) possibility */
8399 
8400   return(vct_to_xen(samples_to_vct(beg, len, cp, pos, NULL, NULL)));
8401 }
8402 
8403 
g_channel_to_vct(Xen samp_0,Xen samps,Xen snd,Xen chn_n,Xen edpos)8404 static Xen g_channel_to_vct(Xen samp_0, Xen samps, Xen snd, Xen chn_n, Xen edpos)
8405 {
8406   #define H_channel_to_vct "(" S_channel_to_vct " :optional (beg 0) (dur len) snd chn edpos): \
8407 return a " S_vct " containing snd channel chn's data starting at beg for dur samps"
8408 
8409   return(samples_to_vct_1(samp_0, samps, snd, chn_n, edpos, S_channel_to_vct));
8410 }
8411 
8412 
g_samples(Xen samp_0,Xen samps,Xen snd,Xen chn_n,Xen edpos)8413 static Xen g_samples(Xen samp_0, Xen samps, Xen snd, Xen chn_n, Xen edpos)
8414 {
8415   #define H_samples "(" S_samples " :optional (start-samp 0) (samps len) snd chn edpos): \
8416 return a " S_vct " containing snd channel chn's samples starting at start-samp for samps samples; edpos is the edit \
8417 history position to read (defaults to current position). snd can be a filename, a mix, a region, or a sound index number."
8418 
8419   chan_info *cp;
8420   mus_long_t beg, len;
8421   int pos;
8422 
8423   Xen_check_type(Xen_is_integer_or_unbound(samp_0) || Xen_is_false(samp_0), samp_0, 1, S_samples, "an integer");
8424   Xen_check_type(Xen_is_integer_or_unbound(samps) || Xen_is_false(samps), samps, 2, S_samples, "an integer");
8425   beg = beg_to_sample(samp_0, S_samples);
8426 
8427   /* -------- a file -------- */
8428   if (Xen_is_string(snd))
8429     {
8430       snd_info *loc_sp = NULL;
8431       int chan = 0, chans;
8432       const char *filename;
8433       mus_float_t *fvals;
8434       vct *v;
8435 
8436       Xen_check_type(Xen_is_integer_boolean_or_unbound(chn_n), chn_n, 4, S_samples, "an integer");
8437       if (Xen_is_integer(chn_n)) chan = Xen_integer_to_C_int(chn_n);
8438       if (chan < 0) return(snd_no_such_channel_error(S_samples, snd, chn_n));
8439 
8440       filename = Xen_string_to_C_string(snd);
8441       if (!mus_file_probe(filename))
8442 	return(snd_no_such_file_error(S_make_sampler, snd));
8443 
8444       if (Xen_is_integer(samps))
8445 	len = Xen_integer_to_C_int(samps);
8446       else len = mus_sound_framples(filename);
8447       if (len <= 0) return(Xen_false);
8448 
8449       chans = mus_sound_chans(filename);
8450       if (chan >= chans)
8451 	return(snd_no_such_channel_error(S_samples, snd, chn_n));
8452 
8453       loc_sp = make_sound_readable(filename, false);
8454       /* cp = loc_sp->chans[chan]; */
8455       v = mus_vct_make(len);
8456       fvals = mus_vct_data(v);
8457       mus_file_to_array(filename, chan, beg, len, fvals);
8458 
8459       completely_free_snd_info(loc_sp);
8460       return(vct_to_xen(v));
8461     }
8462 
8463   /* -------- a mix -------- */
8464   if (xen_is_mix(snd))
8465     return(g_mix_to_vct(snd, samp_0, samps));
8466 
8467 
8468   /* -------- a region -------- */
8469   if (xen_is_region(snd))
8470     return(g_region_to_vct(snd, samp_0, samps, chn_n, Xen_false));
8471 
8472 
8473   /* -------- a sound -------- */
8474   Snd_assert_channel(S_samples, snd, chn_n, 3);
8475   cp = get_cp(snd, chn_n, S_samples);
8476   if (!cp) return(Xen_false);
8477 
8478   pos = to_c_edit_position(cp, edpos, S_samples, 5);
8479   len = dur_to_samples(samps, beg, cp, pos, 2, S_samples);
8480   if (len == 0) return(Xen_false);
8481 
8482   return(vct_to_xen(samples_to_vct(beg, len, cp, pos, NULL, NULL)));
8483 }
8484 
8485 
8486 #if HAVE_SCHEME
g_set_samples_reversed(s7_scheme * sc,s7_pointer args)8487 static Xen g_set_samples_reversed(s7_scheme *sc, s7_pointer args)
8488 {
8489   int len;
8490   len = Xen_list_length(args);
8491   switch (len)
8492     {
8493     case 3:
8494       return(g_set_samples(Xen_list_ref(args, 0), Xen_list_ref(args, 1), Xen_list_ref(args, 2),
8495 			   Xen_undefined, Xen_undefined, Xen_undefined, Xen_undefined, Xen_undefined,
8496 			   Xen_undefined, Xen_undefined));
8497     case 4:
8498       return(g_set_samples(Xen_list_ref(args, 0), Xen_list_ref(args, 1), Xen_list_ref(args, 3), Xen_list_ref(args, 2),
8499 			   Xen_undefined, Xen_undefined, Xen_undefined, Xen_undefined, Xen_undefined, Xen_undefined));
8500     case 5:
8501       return(g_set_samples(Xen_list_ref(args, 0), Xen_list_ref(args, 1), Xen_list_ref(args, 4),
8502 			   Xen_list_ref(args, 2), Xen_list_ref(args, 3),
8503 			   Xen_undefined, Xen_undefined, Xen_undefined, Xen_undefined, Xen_undefined));
8504     case 6:
8505       return(g_set_samples(Xen_list_ref(args, 0), Xen_list_ref(args, 1), Xen_list_ref(args, 5),
8506 			   Xen_list_ref(args, 2), Xen_list_ref(args, 3), Xen_list_ref(args, 4),
8507 			   Xen_undefined, Xen_undefined, Xen_undefined, Xen_undefined));
8508     case 7:
8509       return(g_set_samples(Xen_list_ref(args, 0), Xen_list_ref(args, 1), Xen_list_ref(args, 6),
8510 			   Xen_list_ref(args, 2), Xen_list_ref(args, 3), Xen_list_ref(args, 4),
8511 			   Xen_list_ref(args, 5),
8512 			   Xen_undefined, Xen_undefined, Xen_undefined));
8513     case 8:
8514       return(g_set_samples(Xen_list_ref(args, 0), Xen_list_ref(args, 1), Xen_list_ref(args, 7),
8515 			   Xen_list_ref(args, 2), Xen_list_ref(args, 3), Xen_list_ref(args, 4),
8516 			   Xen_list_ref(args, 5), Xen_list_ref(args, 6),
8517 			   Xen_undefined, Xen_undefined));
8518     case 9:
8519       return(g_set_samples(Xen_list_ref(args, 0), Xen_list_ref(args, 1), Xen_list_ref(args, 8),
8520 			   Xen_list_ref(args, 2), Xen_list_ref(args, 3), Xen_list_ref(args, 4),
8521 			   Xen_list_ref(args, 5), Xen_list_ref(args, 6), Xen_list_ref(args, 7),
8522 			   Xen_undefined));
8523     default:
8524       return(g_set_samples(Xen_list_ref(args, 0), Xen_list_ref(args, 1), Xen_list_ref(args, 9),
8525 			   Xen_list_ref(args, 2), Xen_list_ref(args, 3), Xen_list_ref(args, 4),
8526 			   Xen_list_ref(args, 5), Xen_list_ref(args, 6), Xen_list_ref(args, 7),
8527 			   Xen_list_ref(args, 8)));
8528     }
8529 }
8530 #endif
8531 
8532 
g_change_samples_with_origin(Xen samp_0,Xen samps,Xen origin,Xen vect,Xen snd,Xen chn_n,Xen edpos,Xen date)8533 static Xen g_change_samples_with_origin(Xen samp_0, Xen samps, Xen origin, Xen vect, Xen snd, Xen chn_n, Xen edpos, Xen date)
8534 {
8535   chan_info *cp;
8536   int pos;
8537   mus_long_t beg, len = 0;
8538 
8539   Xen_check_type(Xen_is_llong(samp_0), samp_0, 1, S_change_samples_with_origin, "an integer");
8540   Xen_check_type(Xen_is_llong(samps), samps, 2, S_change_samples_with_origin, "an integer");
8541   Xen_check_type(Xen_is_string(origin), origin, 3, S_change_samples_with_origin, "a string");
8542   Xen_check_type(Xen_is_string(vect), vect, 4, S_change_samples_with_origin, "a filename");
8543 
8544   Snd_assert_channel(S_change_samples_with_origin, snd, chn_n, 5);
8545   cp = get_cp(snd, chn_n, S_change_samples_with_origin);
8546   if (!cp) return(Xen_false);
8547 
8548   beg = beg_to_sample(samp_0, S_change_samples_with_origin);
8549   if (Xen_is_llong(samps)) len = Xen_llong_to_C_llong(samps);
8550   if (len <= 0) return(Xen_false);
8551 
8552   pos = to_c_edit_position(cp, edpos, S_change_samples_with_origin, 7);
8553   check_saved_temp_file("sound", vect, date);
8554 
8555   file_change_samples(beg, len,
8556 		      Xen_string_to_C_string(vect),
8557 		      cp, 0, DONT_DELETE_ME,
8558 		      Xen_string_to_C_string(origin),
8559 		      pos);
8560   update_graph(cp);
8561   return(vect);
8562 }
8563 
8564 
g_insert_sound(Xen file,Xen ubeg,Xen file_chn,Xen snd,Xen chn_n,Xen edpos,Xen auto_delete)8565 static Xen g_insert_sound(Xen file, Xen ubeg, Xen file_chn, Xen snd, Xen chn_n, Xen edpos, Xen auto_delete)
8566 {
8567   #if HAVE_SCHEME
8568     #define insert_sound_example "(" S_insert_sound " \"oboe.snd\" 1000)"
8569   #endif
8570   #if HAVE_RUBY
8571     #define insert_sound_example "insert_sound(\"oboe.snd\", 1000)"
8572   #endif
8573   #if HAVE_FORTH
8574     #define insert_sound_example "\"oboe.snd\" 1000 insert-sound"
8575   #endif
8576 
8577   #define H_insert_sound "(" S_insert_sound " file :optional (beg 0) (file-chan 0) snd chn edpos auto-delete): \
8578 insert channel file-chan of file (or all chans if file-chan is not given) into snd's channel chn at beg or at the cursor \
8579 position.\n  " insert_sound_example "\ninserts all of oboe.snd starting at sample 1000."
8580 
8581   chan_info *cp;
8582   static char *filename = NULL;
8583   int nc;
8584   char *origin;
8585   file_delete_t delete_file = DONT_DELETE_ME;
8586   mus_long_t beg = 0, len;
8587 
8588   Xen_check_type(Xen_is_string(file), file, 1, S_insert_sound, "a string");
8589   Xen_check_type(Xen_is_integer_or_unbound(ubeg), ubeg, 2, S_insert_sound, "an integer");
8590   Xen_check_type(Xen_is_integer_or_unbound(file_chn), file_chn, 3, S_insert_sound, "an integer");
8591 
8592   Snd_assert_channel(S_insert_sound, snd, chn_n, 4);
8593   cp = get_cp(snd, chn_n, S_insert_sound);
8594   if (!cp) return(Xen_false);
8595 
8596   Xen_check_type(Xen_is_integer_boolean_or_unbound(auto_delete), auto_delete, 7, S_insert_sound, "a boolean or an integer");
8597   delete_file = xen_to_file_delete_t(auto_delete, S_insert_sound);
8598 
8599   if (filename) free(filename);
8600   filename = mus_expand_filename(Xen_string_to_C_string(file));
8601   if (!mus_file_probe(filename))
8602     return(snd_no_such_file_error(S_insert_sound, file));
8603 
8604   nc = mus_sound_chans(filename);
8605   if (nc <= 0)
8606     {
8607       Xen_error(BAD_HEADER,
8608 		Xen_list_3(C_string_to_Xen_string(S_insert_sound ": chans <= 0? (~S has ~D chans)"),
8609 			   file,
8610 			   C_int_to_Xen_integer(nc)));
8611     }
8612 
8613   len = mus_sound_framples(filename);
8614   if (len <= 0) return(C_llong_to_Xen_llong(len));
8615 
8616   if (Xen_is_integer(ubeg))
8617     beg = beg_to_sample(ubeg, S_insert_sound);
8618   else beg = cursor_sample(cp);
8619 
8620   if (Xen_is_integer(file_chn))
8621     {
8622       int fchn;
8623       fchn = Xen_integer_to_C_int(file_chn);
8624       if (fchn < 0)
8625 	Xen_error(NO_SUCH_CHANNEL, Xen_list_2(C_string_to_Xen_string(S_insert_sound ": file channel: ~D"), file_chn));
8626 
8627       if (fchn < nc)
8628 	{
8629 #if HAVE_FORTH
8630 	  origin = mus_format("\"%s\" %" print_mus_long " %d %s drop", filename, beg, fchn, S_insert_sound);
8631 #else
8632 	  origin = mus_format("%s" PROC_OPEN "\"%s\"" PROC_SEP "%" print_mus_long PROC_SEP "%d", to_proc_name(S_insert_sound), filename, beg, fchn);
8633 #endif
8634 	  if (file_insert_samples(beg, len, filename, cp, fchn, delete_file, origin,
8635 				  to_c_edit_position(cp, edpos, S_insert_sound, 6)))
8636 	    update_graph(cp);
8637 	  free(origin);
8638 	  return(C_llong_to_Xen_llong(len));
8639 	}
8640       else return(snd_no_such_channel_error(S_insert_sound, file, file_chn));
8641     }
8642   else
8643     {
8644       int i;
8645       snd_info *sp;
8646       sp = cp->sound;
8647       if ((int)sp->nchans < nc) nc = sp->nchans;
8648       for (i = 0; i < nc; i++)
8649 	{
8650 #if HAVE_FORTH
8651 	  origin = mus_format("\"%s\" %" print_mus_long " %d %s drop", filename, beg, i, S_insert_sound);
8652 #else
8653 	  origin = mus_format("%s" PROC_OPEN "\"%s\"" PROC_SEP "%" print_mus_long PROC_SEP "%d", to_proc_name(S_insert_sound), filename, beg, i);
8654 #endif
8655 	  if (file_insert_samples(beg, len, filename, sp->chans[i], i, delete_file, origin,
8656 				  /* this edit_position cannot be optimized out -- each channel may have
8657 				   *   a different edit history, but edpos might be -1 throughout etc.
8658 				   */
8659 				  to_c_edit_position(sp->chans[i], edpos, S_insert_sound, 6)))
8660 	    update_graph(sp->chans[i]);
8661 	  free(origin);
8662 	}
8663       return(C_llong_to_Xen_llong(len));
8664     }
8665   return(Xen_false); /* not reached */
8666 }
8667 
8668 
g_insert_sample(Xen samp_n,Xen val,Xen snd,Xen chn_n,Xen edpos)8669 static Xen g_insert_sample(Xen samp_n, Xen val, Xen snd, Xen chn_n, Xen edpos)
8670 {
8671   #define H_insert_sample "(" S_insert_sample " sample value :optional snd chn edpos): insert 'value' at 'sample' in snd's channel chn"
8672   chan_info *cp;
8673   char *origin;
8674   int pos;
8675   mus_long_t beg;
8676   mus_float_t fval;
8677   mus_float_t ival[1];
8678 
8679   Xen_check_type(Xen_is_integer(samp_n), samp_n, 1, S_insert_sample, "an integer");
8680   Xen_check_type(Xen_is_number(val), val, 2, S_insert_sample, "a number");
8681   Snd_assert_channel(S_insert_sample, snd, chn_n, 3);
8682   cp = get_cp(snd, chn_n, S_insert_sample);
8683   if (!cp) return(Xen_false);
8684   beg = beg_to_sample(samp_n, S_insert_sample);
8685   pos = to_c_edit_position(cp, edpos, S_insert_sample, 5);
8686   fval = Xen_real_to_C_double(val);
8687   ival[0] = fval;
8688 #if HAVE_FORTH
8689   origin = mus_format("%" print_mus_long " %.4f %s drop", beg, fval, S_insert_sample);
8690 #else
8691   origin = mus_format("%s" PROC_OPEN "%" print_mus_long PROC_SEP "%.4f", to_proc_name(S_insert_sample), beg, fval);
8692 #endif
8693   if (insert_samples(beg, 1, ival, cp, origin, pos))
8694     update_graph(cp);
8695   free(origin);
8696   return(val);
8697 }
8698 
8699 
g_insert_samples(Xen samp,Xen samps,Xen vect,Xen snd,Xen chn_n,Xen edpos,Xen auto_delete,Xen caller)8700 static Xen g_insert_samples(Xen samp, Xen samps, Xen vect, Xen snd, Xen chn_n, Xen edpos, Xen auto_delete, Xen caller)
8701 {
8702   #define H_insert_samples "(" S_insert_samples " start-samp samps data :optional snd chn edpos auto-delete origin): \
8703 insert data (either a " S_vct ", a list of samples, or a filename) into snd's channel chn starting at 'start-samp' for 'samps' samples"
8704 
8705   chan_info *cp;
8706   int pos;
8707   char *origin = NULL;
8708   file_delete_t delete_file = DONT_DELETE_ME;
8709   mus_long_t beg, len = 0;
8710 
8711   Xen_check_type(Xen_is_integer(samp), samp, 1, S_insert_samples, "an integer");
8712   Xen_check_type(Xen_is_integer(samps), samps, 2, S_insert_samples, "an integer");
8713   Snd_assert_channel(S_insert_samples, snd, chn_n, 4);
8714   Xen_check_type(Xen_is_string_or_unbound(caller), caller, 8, S_insert_samples, "a string");
8715   cp = get_cp(snd, chn_n, S_insert_samples);
8716   if (!cp) return(Xen_false);
8717 
8718   beg = beg_to_sample(samp, S_insert_samples);
8719   len = Xen_llong_to_C_llong(samps);
8720   if (len <= 0) return(samps);
8721   pos = to_c_edit_position(cp, edpos, S_insert_samples, 6);
8722 
8723   Xen_check_type(Xen_is_integer_boolean_or_unbound(auto_delete), auto_delete, 7, S_insert_samples, "a boolean or an integer");
8724   delete_file = xen_to_file_delete_t(auto_delete, S_insert_samples);
8725 
8726   if (Xen_is_string(caller))
8727     origin = mus_strdup(Xen_string_to_C_string(caller));
8728   if (Xen_is_string(vect))
8729     {
8730       char *filename;
8731       filename = mus_expand_filename(Xen_string_to_C_string(vect));
8732       if (!mus_file_probe(filename))
8733 	{
8734 	  free(filename);
8735 	  return(snd_no_such_file_error(S_insert_samples, vect));
8736 	}
8737       if (mus_sound_framples(filename) <= 0) return(Xen_integer_zero);
8738 #if HAVE_FORTH
8739       if (!origin) origin = mus_format("%" print_mus_long PROC_SEP "%" print_mus_long " \"%s\" %s drop", beg, len, filename, S_insert_samples);
8740 #else
8741       if (!origin) origin = mus_format("%s" PROC_OPEN "%" print_mus_long PROC_SEP "%" print_mus_long PROC_SEP "\"%s\"", to_proc_name(S_insert_samples), beg, len, filename);
8742 #endif
8743       file_insert_samples(beg, len, filename, cp, 0, delete_file, origin, pos);
8744       if (filename) free(filename);
8745     }
8746   else
8747     {
8748       if (mus_is_vct(vect))
8749 	{
8750 	  vct *v;
8751 	  v = Xen_to_vct(vect);
8752 	  if (len > mus_vct_length(v)) len = mus_vct_length(v);
8753 	  if (!origin) origin = mus_strdup(to_proc_name(S_insert_samples));
8754 	  insert_samples(beg, len, mus_vct_data(v), cp, origin, pos);
8755 	}
8756       else
8757 	{
8758 	  int ilen;
8759 	  mus_float_t *ivals;
8760 	  ilen = (int)len;
8761 	  ivals = g_floats_to_samples(vect, &ilen, S_insert_samples, 3);
8762 	  if (ivals)
8763 	    {
8764 	      if (!origin) origin = mus_strdup(to_proc_name(S_insert_samples));
8765 	      insert_samples(beg, (mus_long_t)ilen, ivals, cp, origin, pos);
8766 	      free(ivals);
8767 	    }
8768 	}
8769     }
8770   if (origin) free(origin);
8771   update_graph(cp);
8772   return(C_llong_to_Xen_llong(len));
8773 }
8774 
8775 
g_insert_samples_with_origin(Xen samp,Xen samps,Xen origin,Xen vect,Xen snd,Xen chn_n,Xen edpos,Xen date)8776 static Xen g_insert_samples_with_origin(Xen samp, Xen samps, Xen origin, Xen vect, Xen snd, Xen chn_n, Xen edpos, Xen date)
8777 {
8778   chan_info *cp;
8779   int pos;
8780   mus_long_t beg, len;
8781 
8782   Xen_check_type(Xen_is_integer(samp), samp, 1, S_insert_samples_with_origin, "an integer");
8783   Xen_check_type(Xen_is_integer(samps), samps, 2, S_insert_samples_with_origin, "an integer");
8784   Xen_check_type(Xen_is_string(origin), origin, 3, S_insert_samples_with_origin, "a string");
8785   Xen_check_type(Xen_is_string(vect), vect, 4, S_insert_samples_with_origin, "a filename");
8786 
8787   Snd_assert_channel(S_insert_samples_with_origin, snd, chn_n, 5);
8788   cp = get_cp(snd, chn_n, S_insert_samples_with_origin);
8789   if (!cp) return(Xen_false);
8790 
8791   beg = beg_to_sample(samp, S_insert_samples_with_origin);
8792   len = Xen_llong_to_C_llong(samps);
8793   if (len <= 0) return(samps);
8794 
8795   pos = to_c_edit_position(cp, edpos, S_insert_samples_with_origin, 7);
8796   check_saved_temp_file("sound", vect, date);
8797   file_insert_samples(beg, len, Xen_string_to_C_string(vect), cp, 0, DONT_DELETE_ME, Xen_string_to_C_string(origin), pos);
8798   update_graph(cp);
8799   return(C_llong_to_Xen_llong(len));
8800 }
8801 
8802 
g_delete_sample(Xen samp_n,Xen snd,Xen chn_n,Xen edpos)8803 static Xen g_delete_sample(Xen samp_n, Xen snd, Xen chn_n, Xen edpos)
8804 {
8805   #define H_delete_sample "(" S_delete_sample " samp :optional snd chn edpos): delete sample 'samp' from snd's channel chn"
8806   chan_info *cp;
8807   mus_long_t samp;
8808   int pos;
8809 
8810   Xen_check_type(Xen_is_integer(samp_n), samp_n, 1, S_delete_sample, "an integer");
8811   Snd_assert_channel(S_delete_sample, snd, chn_n, 2);
8812 
8813   cp = get_cp(snd, chn_n, S_delete_sample);
8814   if (!cp) return(Xen_false);
8815   samp = beg_to_sample(samp_n, S_delete_sample);
8816   pos = to_c_edit_position(cp, edpos, S_delete_sample, 4);
8817   if ((samp < 0) || (samp > cp->edits[pos]->samples))
8818     Xen_error(Xen_make_error_type("no-such-sample"),
8819 	      Xen_list_2(C_string_to_Xen_string(S_delete_sample ": no such sample: ~A"),
8820 			 samp_n));
8821 
8822   if (delete_samples(samp, 1, cp, pos))
8823     update_graph(cp);
8824 
8825   return(samp_n);
8826 }
8827 
8828 
g_delete_samples(Xen samp_n,Xen samps,Xen snd,Xen chn_n,Xen edpos)8829 static Xen g_delete_samples(Xen samp_n, Xen samps, Xen snd, Xen chn_n, Xen edpos)
8830 {
8831   #define H_delete_samples "(" S_delete_samples " start-samp samps :optional snd chn edpos): \
8832 delete 'samps' samples from snd's channel chn starting at 'start-samp'"
8833 
8834   chan_info *cp;
8835   int pos;
8836   mus_long_t samp, len;
8837 
8838   Xen_check_type(Xen_is_integer(samp_n), samp_n, 1, S_delete_samples, "an integer");
8839   Xen_check_type(Xen_is_integer(samps), samps, 2, S_delete_samples, "an integer");
8840 
8841   Snd_assert_channel(S_delete_samples, snd, chn_n, 3);
8842   cp = get_cp(snd, chn_n, S_delete_samples);
8843   if (!cp) return(Xen_false);
8844 
8845   pos = to_c_edit_position(cp, edpos, S_delete_samples, 6);
8846   samp = beg_to_sample(samp_n, S_delete_samples);
8847   len = Xen_llong_to_C_llong(samps);
8848   if (len <= 0) return(Xen_false);
8849 
8850   if (delete_samples(samp, len, cp, pos))
8851     update_graph(cp);
8852 
8853   return(samp_n);
8854 }
8855 
8856 
8857 
8858 /* -------- re-direct CLM input (ina etc) to use samplers -------- */
8859 
8860 #include "clm2xen.h"
8861 
8862 static int snd_to_sample_tag = 0;
8863 
8864 typedef struct {
8865   mus_any_class *core;
8866   snd_info *sp;
8867   snd_fd **sfs;
8868   int chans;
8869   mus_long_t *samps;
8870 } snd_to_sample;
8871 
is_snd_to_sample(mus_any * ptr)8872 static bool is_snd_to_sample(mus_any *ptr) {return(mus_type(ptr) == snd_to_sample_tag);}
8873 
snd_to_sample_equalp(mus_any * p1,mus_any * p2)8874 static bool snd_to_sample_equalp(mus_any *p1, mus_any *p2) {return(p1 == p2);}
8875 
snd_to_sample_channels(mus_any * ptr)8876 static int snd_to_sample_channels(mus_any *ptr) {return(((snd_to_sample *)ptr)->chans);}
8877 
snd_to_sample_location(mus_any * ptr)8878 static mus_long_t snd_to_sample_location(mus_any *ptr) {return(((snd_to_sample *)ptr)->samps[0]);}
8879 
snd_to_sample_file_name(mus_any * ptr)8880 static char *snd_to_sample_file_name(mus_any *ptr) {return(((snd_to_sample *)ptr)->sp->filename);}
8881 
snd_to_sample_length(mus_any * ptr)8882 static mus_long_t snd_to_sample_length(mus_any *ptr) {return(current_samples(((snd_to_sample *)ptr)->sp->chans[0]));}
8883 
snd_to_sample_free(mus_any * ptr)8884 static void snd_to_sample_free(mus_any *ptr)
8885 {
8886   snd_to_sample *spl = (snd_to_sample *)ptr;
8887   if (spl->sfs)
8888     {
8889       int i;
8890       for (i = 0; i < spl->chans; i++)
8891 	spl->sfs[i] = free_snd_fd(spl->sfs[i]);
8892       free(spl->sfs);
8893       spl->sfs = NULL;
8894     }
8895   if (spl->samps)
8896     {
8897       free(spl->samps);
8898       spl->samps = NULL;
8899     }
8900   free(spl);
8901 }
8902 
8903 
snd_to_sample_describe(mus_any * ptr)8904 static char *snd_to_sample_describe(mus_any *ptr)
8905 {
8906   char *snd_to_sample_buf = NULL;
8907   int i, len = PRINT_BUFFER_SIZE;
8908   snd_to_sample *spl = (snd_to_sample *)ptr;
8909   char *temp;
8910   if (spl->sfs)
8911     {
8912       len += spl->chans * 8;
8913       for (i = 0; i < spl->chans; i++)
8914 	if (spl->sfs[i])
8915 	  {
8916 	    temp = sampler_to_string(spl->sfs[i]);
8917 	    if (temp)
8918 	      {
8919 		len += mus_strlen(temp);
8920 		free(temp);
8921 	      }
8922 	  }
8923     }
8924   snd_to_sample_buf = (char *)calloc(len, sizeof(char));
8925   snprintf(snd_to_sample_buf, len, "%s reading %s (%d chan%s) at %" print_mus_long ":[",
8926 	       mus_name(ptr),
8927 	       spl->sp->short_filename,
8928 	       spl->chans,
8929 	       (spl->chans > 1) ? "s" : "",
8930 	       spl->samps[0]);
8931   if (spl->sfs)
8932     {
8933       for (i = 0; i < spl->chans; i++)
8934 	if (spl->sfs[i])
8935 	  {
8936 	    temp = sampler_to_string(spl->sfs[i]);
8937 	    if (temp)
8938 	      {
8939 		strcat(snd_to_sample_buf, temp);
8940 		free(temp);
8941 	      }
8942 	    if (i < spl->chans - 1)
8943 	      strcat(snd_to_sample_buf, ", ");
8944 	    else strcat(snd_to_sample_buf, "]");
8945 	  }
8946 	else strcat(snd_to_sample_buf, "nil, ");
8947     }
8948   else strcat(snd_to_sample_buf, "no readers]");
8949   return(snd_to_sample_buf);
8950 }
8951 
8952 
snd_to_sample_read(mus_any * ptr,mus_long_t frample,int chan)8953 static mus_float_t snd_to_sample_read(mus_any *ptr, mus_long_t frample, int chan)
8954 {
8955   snd_to_sample *spl = (snd_to_sample *)ptr;
8956   mus_long_t diff, i;
8957   if (!(spl->sfs))
8958     spl->sfs = (snd_fd **)calloc(spl->chans, sizeof(snd_fd *));
8959   if (!(spl->sfs[chan]))
8960     {
8961       spl->sfs[chan] = init_sample_read(frample, spl->sp->chans[chan], READ_FORWARD);
8962       spl->samps[chan] = frample;
8963       return(next_sample_value(spl->sfs[chan]));
8964     }
8965   diff = frample - spl->samps[chan];
8966   if (diff == 1)
8967     {
8968       spl->samps[chan]++;
8969       return(next_sample_value(spl->sfs[chan]));
8970     }
8971   if (diff > 1)
8972     {
8973       for (i = 1; i < diff; i++) next_sample_value(spl->sfs[chan]); /* just push pointer forward */
8974       spl->samps[chan] = frample;
8975       return(next_sample_value(spl->sfs[chan]));
8976     }
8977   diff = -diff;
8978   for (i = 0; i <= diff; i++) previous_sample_value(spl->sfs[chan]); /* just push pointer backward (one too far) */
8979   spl->samps[chan] = frample;
8980   return(next_sample_value(spl->sfs[chan])); /* always end up going forward (for simpler code) */
8981 }
8982 
8983 
8984 static mus_any_class *snd_to_sample_class;
8985 
make_snd_to_sample(snd_info * sp)8986 static mus_any *make_snd_to_sample(snd_info *sp)
8987 {
8988   snd_to_sample *gen;
8989   gen = (snd_to_sample *)calloc(1, sizeof(snd_to_sample));
8990   gen->core = snd_to_sample_class;
8991   gen->chans = sp->nchans;
8992   gen->sp = sp;
8993   gen->samps = (mus_long_t *)calloc(sp->nchans, sizeof(mus_long_t));
8994   gen->sfs = NULL;           /* created as needed */
8995   return((mus_any *)gen);
8996 }
8997 
8998 
g_is_snd_to_sample(Xen os)8999 static Xen g_is_snd_to_sample(Xen os)
9000 {
9001   #define H_is_snd_to_sample "(" S_is_snd_to_sample " gen): " PROC_TRUE " if gen is an " S_snd_to_sample " generator"
9002   return(C_bool_to_Xen_boolean((mus_is_xen(os)) &&
9003 			       (is_snd_to_sample(Xen_to_mus_any(os)))));
9004 }
9005 
9006 
g_snd_to_sample(Xen os,Xen frample,Xen chan)9007 static Xen g_snd_to_sample(Xen os, Xen frample, Xen chan)
9008 {
9009   #define H_snd_to_sample "(" S_snd_to_sample " gen frample chan): input sample (via snd->sample gen) at frample in channel chan"
9010   Xen_check_type((mus_is_xen(os)) && (is_snd_to_sample(Xen_to_mus_any(os))), os, 1, S_snd_to_sample, "a " S_snd_to_sample " gen");
9011   Xen_check_type(Xen_is_llong(frample), frample, 2, S_snd_to_sample, "an integer");
9012   Xen_check_type(Xen_is_integer_or_unbound(chan), chan, 3, S_snd_to_sample, "an integer");
9013   return(C_double_to_Xen_real(snd_to_sample_read((mus_any *)Xen_to_mus_any(os),
9014 					    Xen_llong_to_C_llong(frample),
9015 					    (Xen_is_integer(chan)) ? Xen_integer_to_C_int(chan) : 0)));
9016 }
9017 
9018 
g_make_snd_to_sample(Xen snd)9019 static Xen g_make_snd_to_sample(Xen snd)
9020 {
9021   #define H_make_snd_to_sample "(" S_make_snd_to_sample " snd): return a new " S_snd_to_sample " (Snd to CLM input) generator"
9022   mus_any *ge;
9023   snd_info *sp;
9024 
9025   Snd_assert_sound(S_make_snd_to_sample, snd, 1);
9026 
9027   sp = get_sp(snd);
9028   if (!sp)
9029     return(snd_no_such_sound_error(S_make_snd_to_sample, snd));
9030 
9031   ge = make_snd_to_sample(sp);
9032   if (ge)
9033     return(mus_xen_to_object(mus_any_to_mus_xen(ge)));
9034   return(Xen_false);
9035 }
9036 
9037 
g_edit_list_to_function(Xen snd,Xen chn,Xen start,Xen end)9038 static Xen g_edit_list_to_function(Xen snd, Xen chn, Xen start, Xen end)
9039 {
9040   #define H_edit_list_to_function "(" S_edit_list_to_function " :optional snd chn start end): function encapsulating edits"
9041   chan_info *cp;
9042   char *funcstr = NULL;
9043   Xen func;
9044   int start_pos = 1, end_pos = -1;
9045 
9046   Snd_assert_channel(S_edit_list_to_function, snd, chn, 1);
9047   cp = get_cp(snd, chn, S_edit_list_to_function);
9048   if (!cp) return(Xen_false);
9049 
9050   Xen_check_type(Xen_is_integer_or_unbound(start), start, 3, S_edit_list_to_function, "an integer");
9051   Xen_check_type(Xen_is_integer_or_unbound(end), end, 4, S_edit_list_to_function, "an integer");
9052 
9053   if (Xen_is_integer(start)) start_pos = Xen_integer_to_C_int(start);
9054   if (start_pos < 0) /* is 0 legal here? */
9055     Xen_out_of_range_error(S_edit_list_to_function, 3, start, "a non-negative integer");
9056   if (Xen_is_integer(end)) end_pos = Xen_integer_to_C_int(end);
9057 
9058   funcstr = edit_list_to_function(cp, start_pos, end_pos);
9059   func = Xen_eval_C_string(funcstr);
9060 
9061 #if HAVE_RUBY
9062   rb_set_property(rb_obj_id(func), C_string_to_Xen_symbol("proc_source"), C_string_to_Xen_string(funcstr));
9063 #endif
9064 
9065 #if HAVE_FORTH
9066   fth_proc_source_set(func, C_string_to_Xen_string(funcstr));
9067 #endif
9068 
9069   free(funcstr);
9070   return(func);
9071 }
9072 
9073 #if HAVE_SCHEME
next_sample_dv(void * o)9074 static s7_double next_sample_dv(void *o)
9075 {
9076   snd_fd *fd = (snd_fd *)o;
9077   return(protected_next_sample(fd));
9078 }
9079 
read_sample_dv(void * o)9080 static s7_double read_sample_dv(void *o)
9081 {
9082   snd_fd *fd = (snd_fd *)o;
9083   return(read_sample(fd));
9084 }
9085 #endif
9086 
9087 
Xen_wrap_5_optional_args(g_make_sampler_w,g_make_sampler)9088 Xen_wrap_5_optional_args(g_make_sampler_w, g_make_sampler)
9089 Xen_wrap_4_optional_args(g_make_region_sampler_w, g_make_region_sampler)
9090 Xen_wrap_1_arg(g_next_sample_w, g_next_sample)
9091 Xen_wrap_1_arg(g_read_sample_w, g_read_sample)
9092 Xen_wrap_2_args(g_read_sample_with_direction_w, g_read_sample_with_direction)
9093 Xen_wrap_1_arg(g_previous_sample_w, g_previous_sample)
9094 Xen_wrap_1_arg(g_free_sampler_w, g_free_sampler)
9095 Xen_wrap_1_arg(g_sampler_home_w, g_sampler_home)
9096 Xen_wrap_1_arg(g_sampler_position_w, g_sampler_position)
9097 Xen_wrap_1_arg(g_is_sampler_w, g_is_sampler)
9098 Xen_wrap_1_arg(g_is_region_sampler_w, g_is_region_sampler)
9099 Xen_wrap_1_arg(g_sampler_at_end_w, g_sampler_at_end)
9100 Xen_wrap_1_arg(g_copy_sampler_w, g_copy_sampler)
9101 Xen_wrap_3_optional_args(g_save_edit_history_w, g_save_edit_history)
9102 Xen_wrap_3_optional_args(g_edit_fragment_w, g_edit_fragment)
9103 Xen_wrap_3_optional_args(g_undo_w, g_undo)
9104 Xen_wrap_3_optional_args(g_redo_w, g_redo)
9105 Xen_wrap_2_optional_args(g_as_one_edit_w, g_as_one_edit)
9106 Xen_wrap_3_optional_args(g_display_edits_w, g_display_edits)
9107 Xen_wrap_3_optional_args(g_edit_tree_w, g_edit_tree)
9108 Xen_wrap_4_optional_args(g_delete_sample_w, g_delete_sample)
9109 Xen_wrap_5_optional_args(g_delete_samples_w, g_delete_samples)
9110 Xen_wrap_5_optional_args(g_insert_sample_w, g_insert_sample)
9111 Xen_wrap_8_optional_args(g_insert_samples_w, g_insert_samples)
9112 Xen_wrap_7_optional_args(g_vct_to_channel_w, g_vct_to_channel)
9113 Xen_wrap_5_optional_args(g_channel_to_vct_w, g_channel_to_vct)
9114 Xen_wrap_7_optional_args(g_insert_sound_w, g_insert_sound)
9115 Xen_wrap_6_optional_args(g_scale_channel_w, g_scale_channel)
9116 Xen_wrap_6_optional_args(g_normalize_channel_w, g_normalize_channel)
9117 Xen_wrap_8_optional_args(g_change_samples_with_origin_w, g_change_samples_with_origin)
9118 Xen_wrap_8_optional_args(g_insert_samples_with_origin_w, g_insert_samples_with_origin)
9119 Xen_wrap_6_optional_args(g_override_samples_with_origin_w, g_override_samples_with_origin)
9120 Xen_wrap_4_optional_args(g_sample_w, g_sample)
9121 #if HAVE_SCHEME
9122 #define g_set_sample_w g_set_sample_reversed
9123 #define g_set_samples_w g_set_samples_reversed
9124 Xen_wrap_5_optional_args(orig_g_set_sample_w, g_set_sample)
9125 #else
9126 Xen_wrap_5_optional_args(g_set_sample_w, g_set_sample)
9127 Xen_wrap_any_args(g_set_samples_w, g_set_samples_any)
9128 #endif
9129 Xen_wrap_any_args(orig_g_set_samples_w, g_set_samples_any)
9130 Xen_wrap_5_optional_args(g_samples_w, g_samples)
9131 Xen_wrap_1_arg(g_is_snd_to_sample_w, g_is_snd_to_sample)
9132 Xen_wrap_3_optional_args(g_snd_to_sample_w, g_snd_to_sample)
9133 Xen_wrap_1_optional_arg(g_make_snd_to_sample_w, g_make_snd_to_sample)
9134 Xen_wrap_4_optional_args(g_edit_list_to_function_w, g_edit_list_to_function)
9135 Xen_wrap_1_arg(g_edit_fragment_type_name_w, g_edit_fragment_type_name)
9136 
9137 void g_init_edits(void)
9138 {
9139 #if HAVE_SCHEME
9140   s7_pointer b, i, p, t, f, fnc, fv, r, s, smp, x, rg, pl_fx;
9141   b = s7_make_symbol(s7, "boolean?");
9142   i = s7_make_symbol(s7, "integer?");
9143   p = s7_make_symbol(s7, "pair?");
9144   f = s7_make_symbol(s7, "float?");
9145   fv = s7_make_symbol(s7, "float-vector?");
9146   r = s7_make_symbol(s7, "real?");
9147   s = s7_make_symbol(s7, "string?");
9148   rg = s7_make_symbol(s7, "region?");
9149   fnc = s7_make_symbol(s7, "procedure?");
9150   smp = s7_make_symbol(s7, "sampler?");
9151   x = s7_make_signature(s7, 2, smp, s7_make_symbol(s7, "mix-sampler?"));
9152   t = s7_t(s7);
9153   /* pl_fx = s7_make_signature(s7, 2, f, x); */
9154   pl_fx = s7_make_signature(s7, 2, f, smp);
9155 
9156   sf_tag = s7_make_c_type(s7, "<sampler>");
9157   s7_c_type_set_gc_free(s7, sf_tag, s7_sf_free);
9158   s7_c_type_set_is_equal(s7, sf_tag, s7_sf_is_equal);
9159   s7_c_type_set_ref(s7, sf_tag, s7_read_sample);
9160   s7_c_type_set_length(s7, sf_tag, length_sf);
9161   s7_c_type_set_to_string(s7, sf_tag, g_sampler_to_string);
9162 #else
9163   sf_tag = Xen_make_object_type("Sampler", sizeof(snd_fd));
9164 #endif
9165 
9166 #if HAVE_RUBY
9167   rb_define_method(sf_tag, "to_s", Xen_procedure_cast print_sf, 0);
9168   rb_define_method(sf_tag, "call", Xen_procedure_cast g_read_sample, 0);
9169 #endif
9170 
9171 #if HAVE_FORTH
9172   fth_set_object_inspect(sf_tag, print_sf);
9173   fth_set_object_free(sf_tag, free_sf);
9174   fth_set_object_apply(sf_tag, Xen_procedure_cast g_read_sample, 0, 0, 0);
9175 #endif
9176 
9177   Xen_define_constant(S_current_edit_position,   AT_CURRENT_EDIT_POSITION,  "represents the current edit history list position (-1)");
9178 
9179   Xen_define_typed_procedure(S_make_sampler,        g_make_sampler_w,        0, 5, 0, H_make_sampler,     s7_make_signature(s7, 6, smp, i, t, t, t, t));
9180   Xen_define_typed_procedure(S_make_region_sampler, g_make_region_sampler_w, 1, 3, 0, H_make_region_sampler, s7_make_signature(s7, 5, smp, rg, i, i, i));
9181   Xen_define_typed_procedure(S_read_sample,         g_read_sample_w,         1, 0, 0, H_read_sample,      pl_fx);
9182   Xen_define_typed_procedure(S_read_sample_with_direction, g_read_sample_with_direction_w, 2, 0, 0, H_read_sample_with_direction, s7_make_signature(s7, 3, f, x, i));
9183   Xen_define_typed_procedure(S_read_region_sample,  g_read_sample_w,         1, 0, 0, H_read_sample,      pl_fx);
9184   Xen_define_typed_procedure(S_next_sample,         g_next_sample_w,         1, 0, 0, H_next_sample,      pl_fx);
9185   Xen_define_typed_procedure(S_previous_sample,     g_previous_sample_w,     1, 0, 0, H_previous_sample,  pl_fx);
9186   Xen_define_typed_procedure(S_free_sampler,        g_free_sampler_w,        1, 0, 0, H_free_sampler,     s7_make_signature(s7, 2, b, x));
9187   Xen_define_typed_procedure(S_sampler_home,        g_sampler_home_w,        1, 0, 0, H_sampler_home,     s7_make_signature(s7, 2, t, x));
9188   Xen_define_typed_procedure(S_is_sampler,          g_is_sampler_w,          1, 0, 0, H_is_sampler,       s7_make_signature(s7, 2, b, t));
9189   Xen_define_typed_procedure(S_is_region_sampler,   g_is_region_sampler_w,   1, 0, 0, H_is_region_sampler, s7_make_signature(s7, 2, b, x));
9190   Xen_define_typed_procedure(S_is_sampler_at_end,   g_sampler_at_end_w,      1, 0, 0, H_sampler_at_end,   s7_make_signature(s7, 2, b, x));
9191   Xen_define_typed_procedure(S_sampler_position,    g_sampler_position_w,    1, 0, 0, H_sampler_position, s7_make_signature(s7, 2, i, x));
9192   Xen_define_typed_procedure(S_copy_sampler,        g_copy_sampler_w,        1, 0, 0, H_copy_sampler,     s7_make_signature(s7, 2, x, x));
9193 
9194   Xen_define_typed_procedure(S_save_edit_history,   g_save_edit_history_w,   1, 2, 0, H_save_edit_history, s7_make_signature(s7, 4, s, s, t, t));
9195   Xen_define_typed_procedure(S_edit_fragment,       g_edit_fragment_w,       0, 3, 0, H_edit_fragment,    s7_make_signature(s7, 4, p, i, t, t));
9196   Xen_define_typed_procedure(S_edit_fragment_type_name,g_edit_fragment_type_name_w, 1, 0, 0, "internal testing function", s7_make_signature(s7, 2, s, i));
9197 
9198   Xen_define_typed_procedure(S_undo,                g_undo_w,                0, 3, 0, H_undo,             s7_make_signature(s7, 4, i, i, t, t));
9199 #if HAVE_RUBY
9200   Xen_define_procedure("undo_edit",                 g_undo_w,                0, 3, 0, H_undo);
9201 #endif
9202   Xen_define_typed_procedure(S_redo,                g_redo_w,                0, 3, 0, H_redo,             s7_make_signature(s7, 4, i, i, t, t));
9203   Xen_define_typed_procedure(S_as_one_edit,         g_as_one_edit_w,         1, 1, 0, H_as_one_edit,      s7_make_signature(s7, 3, t, fnc, s));
9204   Xen_define_typed_procedure(S_display_edits,       g_display_edits_w,       0, 3, 0, H_display_edits,    s7_make_circular_signature(s7, 1, 2, s, t));
9205   Xen_define_typed_procedure(S_edit_tree,           g_edit_tree_w,           0, 3, 0, H_edit_tree,        s7_make_circular_signature(s7, 1, 2, p, t));
9206 
9207   Xen_define_typed_procedure(S_delete_sample,       g_delete_sample_w,       1, 3, 0, H_delete_sample,    s7_make_circular_signature(s7, 2, 3, i, i, t));
9208   Xen_define_typed_procedure(S_delete_samples,      g_delete_samples_w,      2, 3, 0, H_delete_samples,   s7_make_circular_signature(s7, 3, 4, i, i, i, t));
9209   Xen_define_typed_procedure(S_insert_sample,       g_insert_sample_w,       2, 3, 0, H_insert_sample,    s7_make_circular_signature(s7, 2, 3, i, r, t));
9210   Xen_define_typed_procedure(S_insert_samples,      g_insert_samples_w,      3, 5, 0, H_insert_samples,   s7_make_signature(s7, 9, i, i, i, t, t, t, t, b, s));
9211   Xen_define_typed_procedure(S_vct_to_channel,      g_vct_to_channel_w,      1, 6, 0, H_vct_to_channel,   s7_make_circular_signature(s7, 0, 1, t));
9212   Xen_define_typed_procedure(S_channel_to_vct,      g_channel_to_vct_w,      0, 5, 0, H_channel_to_vct,   s7_make_circular_signature(s7, 3, 4, fv, i, t, t));
9213   Xen_define_typed_procedure(S_insert_sound,        g_insert_sound_w,        1, 6, 0, H_insert_sound,     s7_make_circular_signature(s7, 4, 5, i, s, i, i, t));
9214   Xen_define_typed_procedure(S_scale_channel,       g_scale_channel_w,       1, 5, 0, H_scale_channel,    s7_make_circular_signature(s7, 2, 3, r, r, t));
9215   Xen_define_typed_procedure(S_normalize_channel,   g_normalize_channel_w,   1, 5, 0, H_normalize_channel,s7_make_circular_signature(s7, 2, 3, r, r, t));
9216 
9217   Xen_define_typed_procedure(S_change_samples_with_origin,   g_change_samples_with_origin_w,   7, 1, 0, "internal function used in save-state",
9218 			     s7_make_circular_signature(s7, 0, 1, t));
9219   Xen_define_typed_procedure(S_insert_samples_with_origin,   g_insert_samples_with_origin_w,   7, 1, 0, "internal function used in save-state",
9220 			     s7_make_circular_signature(s7, 0, 1, t));
9221   Xen_define_typed_procedure(S_override_samples_with_origin, g_override_samples_with_origin_w, 5, 1, 0, "internal function used in save-state",
9222 			     s7_make_circular_signature(s7, 0, 1, t));
9223 
9224   Xen_define_typed_dilambda(S_sample,  g_sample_w,  H_sample,  S_set S_sample,  g_set_sample_w,  0, 4, 1, 4,
9225 			    s7_make_signature(s7, 5, r, i, t, t, t), s7_make_signature(s7, 6, r, i, t, t, t, r));
9226   Xen_define_typed_dilambda(S_samples, g_samples_w, H_samples, S_set S_samples, g_set_samples_w, 0, 5, 3, 7,
9227 			    s7_make_circular_signature(s7, 2, 3, t, i, t), s7_make_circular_signature(s7, 0, 1, t));
9228 
9229 #if HAVE_SCHEME
9230   Xen_define_typed_procedure("set-sample",          orig_g_set_sample_w,     2, 3, 0, H_sample,      s7_make_circular_signature(s7, 3, 4, r, i, r, t));
9231 #endif
9232   Xen_define_typed_procedure("set-samples",         orig_g_set_samples_w,    0, 0, 1, H_set_samples, s7_make_circular_signature(s7, 3, 4, r, i, r, t));
9233 
9234   Xen_define_typed_procedure(S_is_snd_to_sample,    g_is_snd_to_sample_w,    1, 0, 0, H_is_snd_to_sample,   s7_make_signature(s7, 2, b, t));
9235   Xen_define_typed_procedure(S_make_snd_to_sample,  g_make_snd_to_sample_w,  0, 1, 0, H_make_snd_to_sample, s7_make_signature(s7, 2, t, t));
9236   Xen_define_typed_procedure(S_snd_to_sample,       g_snd_to_sample_w,       2, 1, 0, H_snd_to_sample,      s7_make_signature(s7, 4, f, t, i, i));
9237   Xen_define_unsafe_typed_procedure(S_edit_list_to_function, g_edit_list_to_function_w, 0, 4, 0, H_edit_list_to_function, s7_make_signature(s7, 5, t, t, t, i, i));
9238   /* not safe because it calls eval-string */
9239 
9240   #define H_save_hook S_save_hook " (snd name): called each time a file is about to be saved. \
9241 If it returns " PROC_TRUE ", the file is not saved.  'name' is " PROC_FALSE " unless the file is being saved under a new name (as in sound-save-as)."
9242 
9243   save_hook = Xen_define_hook(S_save_hook, "(make-hook 'snd 'name)", 2, H_save_hook);
9244 
9245   #define H_save_state_hook S_save_state_hook " (temp-filename): called each time the " S_save_state " \
9246 mechanism is about to create a new temporary file to save some edit history sample values. \
9247 temp-filename is the current file. \
9248 If the hook returns a string, it is treated as the new temp filename.  This hook provides a way to \
9249 keep track of which files are in a given saved state batch, and a way to rename or redirect those files."
9250 
9251   save_state_hook = Xen_define_hook(S_save_state_hook, "(make-hook 'name)", 1, H_save_state_hook);
9252 
9253 
9254   snd_to_sample_tag = mus_make_generator_type();
9255   snd_to_sample_class = mus_make_generator(snd_to_sample_tag, (char *)S_snd_to_sample, snd_to_sample_free, snd_to_sample_describe, snd_to_sample_equalp);
9256   mus_generator_set_length(snd_to_sample_class, snd_to_sample_length);
9257   mus_generator_set_channels(snd_to_sample_class, snd_to_sample_channels);
9258   mus_generator_set_read_sample(snd_to_sample_class, snd_to_sample_read);
9259   mus_generator_set_file_name(snd_to_sample_class, snd_to_sample_file_name);
9260   mus_generator_set_location(snd_to_sample_class, snd_to_sample_location);
9261   mus_generator_set_extended_type(snd_to_sample_class, MUS_INPUT);
9262 
9263 #if HAVE_SCHEME
9264   {
9265     s7_pointer f;
9266     edit_finish = s7_make_function(s7, "(finish-as-one-edit)", g_edit_finish, 0, 0, false, "");
9267 
9268     f = s7_name_to_value(s7, "next-sample");
9269     s7_set_d_v_function(s7, f, next_sample_dv);
9270 
9271     f = s7_name_to_value(s7, "read-sample");
9272     s7_set_d_v_function(s7, f, read_sample_dv);
9273   }
9274 #endif
9275 
9276 #if DEBUG_EDIT_TABLES
9277   /* consistency checks for the accessor state table */
9278   init_hit_entries();
9279   check_type_info_entry(ED_SIMPLE, 0, 0, 0, false);
9280   check_type_info_entry(ED_ZERO, 0, 0, 0, true);
9281   report_unhit_entries();
9282 #endif
9283 }
9284 
9285 /* from Anders:
9286 
9287 How to have the trees displayed in a meaningful manner,
9288 especially related to common-music type work?  Maybe the edits
9289 could be given optional names?  ie. "inversed", "prolongue-1",
9290 "scale-1" etc. - and a more specialised graphing system put
9291 together.
9292 
9293 If all this is based on closures, doing "Select-All", should it
9294 write out a temp-file of the state as now?  And how to import it
9295 into some arbitrary place in the tree again?  Would pasting it in
9296 be analogous to take a certain stage of the edit-history and
9297 append the rest?
9298 */
9299