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