"Fossies" - the Fresh Open Source Software Archive

Member "snd-20.9/snd-edits.c" (19 Nov 2020, 279104 Bytes) of package /linux/misc/snd-20.9.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "snd-edits.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 20.8_vs_20.9.

    1 #include "snd.h"
    2 
    3 static Xen save_hook;
    4 
    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 
   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 
   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 
   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 
   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 
   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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  603 static mus_float_t end_sample_value(snd_fd *ignore) {return(0.0);}
  604 
  605 
  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 
  614 static mus_float_t next_sample_value_unchecked(snd_fd *sf) 
  615 {
  616   return(sf->data[sf->loc++] * sf->fscaler);
  617 }
  618 
  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) ;
  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);
  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);
  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) ;
  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);
  657 mus_float_t previous_sample_value_unscaled_and_unchecked(snd_fd *sf) 
  658 {
  659   return(sf->data[sf->loc--]);
  660 }
  661 
  662 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 
 1079 static bool zero_op(int type)
 1080 {
 1081   return(type_info[type].zero);
 1082 }
 1083 
 1084 
 1085 static bool ramp_op(int type)
 1086 {
 1087   return(type_info[type].ramps);
 1088 }
 1089 
 1090 
 1091 static bool is_mix_op(int type)
 1092 {
 1093   return(type_info[type].mixes);
 1094 }
 1095 
 1096 static bool is_unmixable_op(int type)
 1097 {
 1098   return(type_info[type].subtract_mix != -1);
 1099 }
 1100 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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  */
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 1526 mus_long_t edit_changes_begin_at(chan_info *cp, int edpos)
 1527 {
 1528   return(cp->edits[edpos]->beg);
 1529 }
 1530 
 1531 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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  */
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 2929 void backup_edit_list(chan_info *cp)
 2930 {
 2931   backup_edit_list_1(cp, false);
 2932 }
 2933 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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)