1 #include "snd.h"
2
3 static void mix_set_file_name(int id, int chans, const char *name);
4
mix_vct_untagged(vct * v,chan_info * cp,mus_long_t beg,const char * origin)5 static bool mix_vct_untagged(vct *v, chan_info *cp, mus_long_t beg, const char *origin)
6 {
7 mus_float_t *data, *vdata;
8 int len;
9 snd_fd *sf;
10 bool result = false;
11
12 len = mus_vct_length(v);
13 vdata = mus_vct_data(v);
14 data = (mus_float_t *)calloc(len, sizeof(mus_float_t)); /* don't add into v->data! */
15
16 sf = init_sample_read(beg, cp, READ_FORWARD);
17 samples_to_vct_with_reader(len, data, sf);
18 mus_add_floats(data, vdata, len);
19 free_snd_fd(sf);
20
21 result = change_samples(beg, len, data, cp, origin, cp->edit_ctr, -1.0); /* cp->edit_ctr since mix-vct has no edpos arg, similarly mix */
22 if (result) update_graph(cp);
23
24 free(data);
25 return(result);
26 }
27
28
29 mus_float_t next_sample_value_unscaled(snd_fd *sf);
30 mus_float_t next_sound(snd_fd *sf);
31
mix_file_untagged(const char * filename,int in_chan,chan_info * cp,mus_long_t beg,mus_long_t num,file_delete_t auto_delete,const char * origin)32 static bool mix_file_untagged(const char *filename, int in_chan, chan_info *cp, mus_long_t beg, mus_long_t num, file_delete_t auto_delete, const char *origin)
33 {
34 file_info *ihdr, *ohdr;
35 char *ofile;
36 int ofd, ifd = -1;
37 io_error_t io_err = IO_NO_ERROR;
38 snd_fd *sf = NULL;
39 mus_long_t i, j, size, in_chans;
40 int err = 0;
41 mus_float_t **data;
42 mus_float_t *chandata;
43
44 if ((num <= 0) || (!is_editable(cp)))
45 return(false);
46
47 ihdr = make_file_info(filename, FILE_READ_ONLY, FILE_NOT_SELECTED);
48 if (!ihdr) return(false);
49
50 if (in_chan >= ihdr->chans)
51 {
52 free_file_info(ihdr);
53 return(false);
54 }
55
56 ofile = snd_tempnam();
57 ohdr = make_temp_header(ofile, snd_srate(cp->sound), 1, 0, (char *)origin);
58 ofd = open_temp_file(ofile, 1, ohdr, &io_err);
59 if (ofd == -1)
60 {
61 free_file_info(ihdr);
62 free_file_info(ohdr);
63 snd_error("%s mix temp file %s: %s",
64 (io_err != IO_NO_ERROR) ? io_error_name(io_err) : "can't open",
65 ofile,
66 snd_open_strerror());
67 return(false);
68 }
69
70 if ((disk_has_space(num * mus_bytes_per_sample(ohdr->sample_type), ofile)) != DISK_SPACE_OK)
71 return(false);
72
73 sf = init_sample_read(beg, cp, READ_FORWARD);
74 if (sf) ifd = snd_open_read(filename);
75 if ((!sf) || (ifd < 0))
76 {
77 if (sf) free_snd_fd(sf);
78 free_file_info(ihdr);
79 free_file_info(ohdr);
80 mus_file_close(ofd);
81 snd_remove(ofile, REMOVE_FROM_CACHE);
82 free(ofile);
83 return(false);
84 }
85
86 if (beg < 0) beg = 0;
87 in_chans = ihdr->chans;
88
89 snd_file_open_descriptors(ifd, filename,
90 ihdr->sample_type,
91 ihdr->data_location,
92 ihdr->chans,
93 ihdr->type);
94 during_open(ifd, filename, SND_MIX_FILE);
95 if (num < MAX_BUFFER_SIZE) size = num; else size = MAX_BUFFER_SIZE;
96
97 data = (mus_float_t **)calloc(in_chans, sizeof(mus_float_t *));
98 data[in_chan] = (mus_float_t *)calloc(size, sizeof(mus_float_t));
99 chandata = data[in_chan];
100
101 sampler_set_safe(sf, num);
102 lseek(ofd, ohdr->data_location, SEEK_SET);
103 lseek(ifd, ihdr->data_location, SEEK_SET);
104 mus_file_read_chans(ifd, 0, size, in_chans, data, data);
105
106 for (i = 0; i < num; i += size)
107 {
108 mus_long_t kdur;
109 kdur = num - i;
110 if (kdur > size) kdur = size;
111 if (sf->runf == next_sample_value_unscaled)
112 {
113 #if (!WITH_VECTORIZE)
114 for (j = 0; j < kdur; j++)
115 chandata[j] += (sf->loc > sf->last) ? next_sound(sf) : sf->data[sf->loc++];
116 #else
117 for (j = 0; j < kdur; )
118 {
119 mus_long_t ksize;
120 ksize = sf->last - sf->loc + 1;
121 if (ksize == 1)
122 chandata[j++] = next_sound(sf);
123 else
124 {
125 if (j + ksize > kdur) ksize = kdur - j;
126 mus_copy_floats((mus_float_t *)(chandata + j), (mus_float_t *)(sf->data + sf->loc), ksize);
127 j += ksize;
128 if (j < kdur)
129 chandata[j++] = next_sound(sf);
130 }
131 }
132 #endif
133 }
134 else
135 {
136 for (j = 0; j < kdur; j++)
137 chandata[j] += read_sample(sf);
138 }
139 err = mus_file_write(ofd, 0, kdur - 1, 1, &chandata);
140 mus_file_read_chans(ifd, i, size, in_chans, data, data);
141 if (err != MUS_NO_ERROR) break;
142 }
143
144 close_temp_file(ofile, ofd, ohdr->type, num * mus_bytes_per_sample(ohdr->sample_type));
145 mus_file_close(ifd);
146 free_snd_fd(sf);
147 free(data[in_chan]);
148 free(data);
149 free_file_info(ihdr);
150 free_file_info(ohdr);
151 file_change_samples(beg, num, ofile, cp, 0, DELETE_ME, origin, cp->edit_ctr);
152 if (ofile) free(ofile);
153
154 if (auto_delete == DELETE_ME)
155 snd_remove(filename, REMOVE_FROM_CACHE);
156
157 update_graph(cp);
158 return(true);
159 }
160
161
mix_complete_file_at_cursor(snd_info * sp,const char * filename)162 int mix_complete_file_at_cursor(snd_info *sp, const char *filename)
163 {
164 if ((sp) && (filename) && (*filename))
165 {
166 chan_info *cp;
167 int err = 0;
168 char *fullname;
169 fullname = mus_expand_filename(filename);
170 cp = any_selected_channel(sp);
171 err = mix_complete_file(sp, cursor_sample(cp), fullname, with_mix_tags(ss), DONT_DELETE_ME, MIX_FOLLOWS_SYNC, NULL);
172 if (err == MIX_FILE_NO_FILE)
173 snd_error("can't mix file: %s, %s", filename, snd_io_strerror());
174 else
175 {
176 if (err == MIX_FILE_NO_MIX)
177 snd_error("no data to mix in %s", filename);
178 }
179 if (fullname) free(fullname);
180 return(err);
181 }
182 return(MIX_FILE_NO_SP);
183 }
184
185
drag_and_drop_mix_at_x_y(int data,const char * filename,int x,int y)186 void drag_and_drop_mix_at_x_y(int data, const char *filename, int x, int y)
187 {
188 int chn, snd;
189 chn = unpack_channel(data);
190 snd = unpack_sound(data);
191 if ((snd >= 0) &&
192 (snd < ss->max_sounds) &&
193 (snd_ok(ss->sounds[snd])) &&
194 (chn >= 0) &&
195 (chn < (int)(ss->sounds[snd]->nchans)) &&
196 (mus_file_probe(filename)))
197 {
198 snd_info *sp;
199 chan_info *cp;
200 mus_long_t sample;
201 char *fullname = NULL;
202 sp = ss->sounds[snd];
203 cp = sp->chans[chn];
204 if ((sp->nchans > 1) &&
205 (sp->channel_style == CHANNELS_COMBINED))
206 {
207 cp = which_channel(sp, y);
208 chn = cp->chan;
209 }
210 select_channel(sp, chn);
211 sample = snd_round_mus_long_t(ungrf_x(cp->axis, x) * (double)(snd_srate(sp)));
212 if (sample < 0) sample = 0;
213 fullname = mus_expand_filename(filename);
214 mix_complete_file(sp, sample, fullname, with_mix_tags(ss), DONT_DELETE_ME, MIX_FOLLOWS_SYNC, NULL);
215 if (fullname) free(fullname);
216 }
217 }
218
219
220 static int mix_infos_ctr = 0;
221
mix_complete_file(snd_info * sp,mus_long_t beg,const char * fullname,bool with_tag,file_delete_t auto_delete,mix_sync_t all_chans,int * out_chans)222 int mix_complete_file(snd_info *sp, mus_long_t beg, const char *fullname, bool with_tag, file_delete_t auto_delete, mix_sync_t all_chans, int *out_chans)
223 {
224 chan_info *cp;
225 chan_info **cps = NULL;
226 int chans, id = MIX_FILE_NO_MIX, old_sync;
227 mus_long_t len;
228 sync_info *si = NULL;
229
230 len = mus_sound_framples(fullname);
231 if (len < 0) return(MIX_FILE_NO_FILE);
232 if (len == 0) return(MIX_FILE_NO_MIX);
233
234 cp = any_selected_channel(sp);
235 old_sync = sp->sync;
236 if ((old_sync == 0) &&
237 (all_chans == MIX_SETS_SYNC_LOCALLY))
238 {
239 sp->sync = ss->sound_sync_max + 1;
240 ss->sound_sync_max++;
241 }
242 if (sp->sync != 0)
243 {
244 si = snd_sync(sp->sync);
245 cps = si->cps;
246 chans = si->chans;
247 }
248 else
249 {
250 cps = (chan_info **)calloc(1, sizeof(chan_info *));
251 cps[0] = cp;
252 chans = 1;
253 }
254
255 id = mix_file(beg, len, chans, cps, fullname, auto_delete, NULL, with_tag, 0);
256 if (si)
257 free_sync_info(si);
258 else
259 {
260 if (cps)
261 free(cps);
262 }
263 sp->sync = old_sync;
264
265 if (mix_exists(id)) /* bugfix thanks to Tito Latini, 18-Jan-17 */
266 {
267 if (chans > 1)
268 {
269 chans = mix_infos_ctr - id;
270 if (chans > 1)
271 {
272 int i, sync = GET_NEW_SYNC;
273 for (i = 0; i < chans; i++)
274 sync = mix_set_sync_from_id(id + i, sync);
275 }
276 }
277 if (out_chans) (*out_chans) = chans;
278 mix_set_file_name(id, chans, fullname);
279 }
280
281 return(id);
282 }
283
284
b2s(bool val)285 static const char *b2s(bool val)
286 {
287 return((val) ? PROC_TRUE : PROC_FALSE); /* cast needed by g++ > 3.4 */
288 }
289
290
tagged_mix_to_string(const char * mixinfile,mus_long_t beg,int file_channel,bool delete_file)291 static char *tagged_mix_to_string(const char *mixinfile, mus_long_t beg, int file_channel, bool delete_file)
292 {
293 #if HAVE_FORTH
294 return(mus_format("\"%s\" %" print_mus_long " %d snd chn %s %s %s to -mix-%d", mixinfile, beg, file_channel, b2s(true), b2s(delete_file), S_mix, mix_infos_ctr));
295 #endif
296 #if HAVE_SCHEME
297 return(mus_format("(varlet -env- '-mix-%d (%s \"%s\" %" print_mus_long " %d snd chn %s %s))", mix_infos_ctr, S_mix, mixinfile, beg, file_channel, b2s(true), b2s(delete_file)));
298 #endif
299 #if HAVE_RUBY
300 return(mus_format("_mix_%d = %s(\"%s\", %" print_mus_long ", %d, snd, chn, %s, %s)", mix_infos_ctr, to_proc_name(S_mix), mixinfile, beg, file_channel, b2s(true), b2s(delete_file)));
301 #endif
302 #if (!HAVE_EXTENSION_LANGUAGE)
303 return(NULL);
304 #endif
305 }
306
307
untagged_mix_to_string(const char * mixinfile,mus_long_t beg,int file_channel,bool delete_file)308 static char *untagged_mix_to_string(const char *mixinfile, mus_long_t beg, int file_channel, bool delete_file)
309 {
310 #if HAVE_FORTH
311 return(mus_format("\"%s\" %" print_mus_long " %d snd chn %s %s %s", mixinfile, beg, file_channel, b2s(false), b2s(delete_file), S_mix));
312 #endif
313 #if HAVE_SCHEME
314 return(mus_format("(%s \"%s\" %" print_mus_long " %d snd chn %s %s)", S_mix, mixinfile, beg, file_channel, b2s(false), b2s(delete_file)));
315 #endif
316 #if HAVE_RUBY
317 return(mus_format("%s(\"%s\", %" print_mus_long ", %d, snd, chn, %s, %s)", to_proc_name(S_mix), mixinfile, beg, file_channel, b2s(false), b2s(delete_file)));
318 #endif
319 #if (!HAVE_EXTENSION_LANGUAGE)
320 return(NULL);
321 #endif
322 }
323
324
mix_file(mus_long_t beg,mus_long_t num,int chans,chan_info ** cps,const char * mixinfile,file_delete_t temp,const char * origin,bool with_tag,int start_chan)325 int mix_file(mus_long_t beg, mus_long_t num, int chans, chan_info **cps, const char *mixinfile, file_delete_t temp, const char *origin, bool with_tag, int start_chan)
326 {
327 /* used in mix_selection and paste(mix)_region, and in mix_complete_file */
328
329 int i, id = MIX_FILE_NO_MIX, in_chans;
330 #if HAVE_SCHEME
331 int is_mix_selection = ((origin) && (strncmp(origin, "-mix-selection-", 15) == 0));
332 #endif
333 char *new_origin = NULL;
334
335 in_chans = mus_sound_chans(mixinfile);
336 if (chans > in_chans) chans = in_chans;
337
338 if (temp == MULTICHANNEL_DELETION)
339 remember_temp(mixinfile, in_chans);
340
341 for (i = 0; i < chans; i++)
342 {
343 chan_info *cp;
344 cp = cps[i];
345
346 #if HAVE_SCHEME
347 if (is_mix_selection)
348 new_origin = mus_format("(varlet -env- '-mix-%d (%s %d %d))", mix_infos_ctr, origin,
349 start_chan + i, start_chan);
350 #endif
351 if ((!with_tag) ||
352 (!virtual_mix_ok(cp, cp->edit_ctr)))
353 {
354 /* not a virtual mix */
355 if (!origin)
356 new_origin = untagged_mix_to_string(mixinfile, beg, start_chan + i, temp != DONT_DELETE_ME);
357 else
358 {
359 if (!new_origin)
360 new_origin = mus_strdup(origin);
361 }
362 mix_file_untagged(mixinfile, i + start_chan, cp, beg, num, temp, new_origin);
363 }
364 else
365 {
366 /* virtual mix */
367 int cur_id;
368 if (!origin)
369 new_origin = tagged_mix_to_string(mixinfile, beg, start_chan + i, temp != DONT_DELETE_ME);
370 else
371 {
372 if (!new_origin)
373 new_origin = mus_strdup(origin);
374 }
375 cur_id = mix_file_with_tag(cp, mixinfile, i + start_chan, beg, temp, new_origin);
376 if (id == MIX_FILE_NO_MIX) id = cur_id;
377 }
378
379 if (new_origin)
380 {
381 free(new_origin);
382 new_origin = NULL;
383 }
384 }
385
386 for (i = 0; i < chans; i++)
387 update_graph(cps[i]);
388 return(id);
389 }
390
391
392
free_mix_state(mix_state * ms)393 static mix_state *free_mix_state(mix_state *ms)
394 {
395 if (ms)
396 {
397 if (ms->amp_env)
398 ms->amp_env = free_env(ms->amp_env);
399 free(ms);
400 }
401 return(NULL);
402 }
403
404
make_mix_state(int id,int index,mus_long_t beg,mus_long_t len)405 static mix_state *make_mix_state(int id, int index, mus_long_t beg, mus_long_t len)
406 {
407 mix_state *ms;
408 ms = (mix_state *)calloc(1, sizeof(mix_state));
409 ms->mix_id = id;
410 ms->scaler = 1.0;
411 ms->speed = 1.0;
412 ms->beg = beg;
413 ms->len = len;
414 ms->amp_env = NULL;
415 ms->index = index;
416 return(ms);
417 }
418
419
copy_mix_state(mix_state * old_ms)420 mix_state *copy_mix_state(mix_state *old_ms)
421 {
422 mix_state *ms;
423 ms = (mix_state *)calloc(1, sizeof(mix_state));
424 ms->mix_id = old_ms->mix_id;
425 ms->scaler = old_ms->scaler;
426 ms->speed = old_ms->speed;
427 ms->beg = old_ms->beg;
428 ms->len = old_ms->len;
429 ms->index = old_ms->index;
430 ms->amp_env = copy_env(old_ms->amp_env); /* this is the amp env (not the peak env) */
431 return(ms);
432 }
433
434
435 /* this is the edit list header for the list of mix states: ed_list->mixes (void* in snd-1.h) */
436 typedef struct {
437 int size;
438 mix_state **list;
439 } mix_list;
440
free_ed_mixes(void * ptr)441 void free_ed_mixes(void *ptr)
442 {
443 if (ptr)
444 {
445 int i;
446 mix_list *mxl = (mix_list *)ptr;
447 for (i = 0; i < mxl->size; i++)
448 if (mxl->list[i])
449 mxl->list[i] = free_mix_state(mxl->list[i]);
450 free(mxl->list);
451 free(mxl);
452 }
453 }
454
455
add_ed_mix(ed_list * ed,mix_state * ms)456 void add_ed_mix(ed_list *ed, mix_state *ms)
457 {
458 mix_list *mxl;
459 int loc = -1;
460 if (!(ed->mixes))
461 {
462 ed->mixes = (mix_list *)calloc(1, sizeof(mix_list));
463 mxl = (mix_list *)(ed->mixes);
464 mxl->size = 2;
465 mxl->list = (mix_state **)calloc(mxl->size, sizeof(mix_state *));
466 loc = 0;
467 }
468 else
469 {
470 int i;
471 mxl = (mix_list *)(ed->mixes);
472 for (i = 0; i < mxl->size; i++)
473 if (!mxl->list[i])
474 {
475 loc = i;
476 break;
477 }
478 if (loc == -1)
479 {
480 loc = mxl->size;
481 mxl->size *= 2;
482 mxl->list = (mix_state **)realloc(mxl->list, mxl->size * sizeof(mix_state *));
483 for (i = loc; i < mxl->size; i++) mxl->list[i] = NULL;
484 }
485 }
486 mxl->list[loc] = ms;
487 }
488
489
preload_mixes(mix_state ** mixes,int low_id,ed_list * ed)490 void preload_mixes(mix_state **mixes, int low_id, ed_list *ed)
491 {
492 mix_list *mxl;
493 mxl = (mix_list *)(ed->mixes);
494 if (mxl)
495 {
496 int i;
497 for (i = 0; i < mxl->size; i++)
498 if (mxl->list[i])
499 mixes[mxl->list[i]->mix_id - low_id] = mxl->list[i];
500 }
501 }
502
503
ed_mix_state(ed_list * ed,int mix_id)504 static mix_state *ed_mix_state(ed_list *ed, int mix_id)
505 {
506 mix_list *mxl;
507 mxl = (mix_list *)(ed->mixes);
508 if (mxl)
509 {
510 int i;
511 for (i = 0; i < mxl->size; i++)
512 if ((mxl->list[i]) &&
513 (mxl->list[i]->mix_id == mix_id))
514 return(mxl->list[i]);
515 }
516 return(NULL);
517 }
518
519
520 /* these are the nominally unchanging fields in a mix (they don't follow the edit lists) */
521
522 #define MIX_TAG_ERASED -1
523 #define ORIGINAL_SYNC_UNSET -1
524
525 typedef struct {
526 int id;
527 char *name;
528 chan_info *cp;
529 int original_index; /* index into cp->sounds array for original mix data */
530 char *in_filename;
531 mus_long_t in_samps;
532 int in_chan;
533 int tag_x, tag_y;
534 int sync, original_sync;
535 file_delete_t temporary; /* in-filename was written by us and needs to be deleted when mix state is deleted */
536 peak_env_info *peak_env;
537 Xen properties;
538 int properties_gc_loc;
539 color_t color, original_color;
540 int x, y; /* these are needed to know where to erase while dragging the tag */
541 } mix_info;
542
543
current_mix_state(mix_info * md)544 static mix_state *current_mix_state(mix_info *md)
545 {
546 if (md)
547 return(ed_mix_state(md->cp->edits[md->cp->edit_ctr], md->id));
548 return(NULL);
549 }
550
551
552 #if 0
553 /* if edpos args to various mix field (getters anyway), mix? mixes make-mix-sampler... */
554
555 static mix_state *mix_state_at_edpos(mix_info *md, int edpos)
556 {
557 if (md)
558 {
559 if (edpos == AT_CURRENT_EDIT_POSITION)
560 return(current_mix_state(md));
561 else
562 {
563 chan_info *cp;
564 cp = md->cp;
565 if ((edpos >= 0) &&
566 (edpos < cp->edit_size) &&
567 (cp->edits[edpos]))
568 return(ed_mix_state(cp->edits[edpos], md->id));
569 }
570 }
571 return(NULL);
572 }
573 #endif
574
575
576 /* for ease of access, all tagged mixes that are still accessible (perhaps via undo from fixed state, etc)
577 * are saved in an array indexed by the mix id. A mix is "ok" if it's still in this array, and "active"
578 * if it's represented in the current edit's mix list.
579 */
580
581 #define MIX_INFO_INCREMENT 16
582 static mix_info **mix_infos = NULL;
583 static int mix_infos_size = 0;
584
mix_exists(int n)585 bool mix_exists(int n)
586 {
587 return((n >= 0) &&
588 (n < mix_infos_size) &&
589 (mix_infos[n]));
590 }
591
592
mix_is_active(int n)593 bool mix_is_active(int n)
594 {
595 return((mix_exists(n)) &&
596 (current_mix_state(mix_infos[n])));
597 }
598
599
md_from_id(int n)600 static mix_info *md_from_id(int n)
601 {
602 if (mix_exists(n))
603 return(mix_infos[n]);
604 return(NULL);
605 }
606
607
any_mix_id(void)608 int any_mix_id(void)
609 {
610 int i;
611 for (i = 0; i < mix_infos_ctr; i++)
612 if (mix_is_active(i))
613 return(i);
614 return(INVALID_MIX_ID);
615 }
616
617
next_mix_id(int id)618 int next_mix_id(int id)
619 {
620 int i;
621 for (i = id + 1; i < mix_infos_ctr; i++)
622 if (mix_is_active(i))
623 return(i);
624 return(INVALID_MIX_ID);
625 }
626
627
previous_mix_id(int id)628 int previous_mix_id(int id)
629 {
630 int i, top;
631 top = id - 1;
632 if (top >= mix_infos_ctr) top = mix_infos_ctr - 1;
633 for (i = top; i >= 0; i--)
634 if (mix_is_active(i))
635 return(i);
636 return(INVALID_MIX_ID);
637 }
638
639
lowest_mix_id(void)640 int lowest_mix_id(void)
641 {
642 int i;
643 for (i = 0; i < mix_infos_ctr; i++)
644 if (mix_infos[i])
645 return(i);
646 return(INVALID_MIX_ID);
647 }
648
649
highest_mix_id(void)650 int highest_mix_id(void)
651 {
652 int i;
653 for (i = mix_infos_ctr - 1; i >= 0; i--)
654 if (mix_infos[i])
655 return(i);
656 return(INVALID_MIX_ID);
657 }
658
659
free_mix_info(mix_info * md)660 static mix_info *free_mix_info(mix_info *md)
661 {
662 if (md)
663 {
664 if (md->name) {free(md->name); md->name = NULL;}
665 mix_infos[md->id] = NULL;
666 if (md->temporary == DELETE_ME)
667 {
668 if (mus_file_probe(md->in_filename))
669 snd_remove(md->in_filename, REMOVE_FROM_CACHE);
670 }
671 if (md->in_filename) {free(md->in_filename); md->in_filename = NULL;}
672 if (md->properties_gc_loc != NOT_A_GC_LOC)
673 {
674 snd_unprotect_at(md->properties_gc_loc);
675 md->properties_gc_loc = NOT_A_GC_LOC;
676 md->properties = Xen_false;
677 }
678 if (md->peak_env)
679 md->peak_env = free_peak_env_info(md->peak_env);
680 free(md);
681 }
682 return(NULL);
683 }
684
685
free_channel_mixes(chan_info * cp)686 void free_channel_mixes(chan_info *cp)
687 {
688 /* called in snd-data.c during chan_info cleanup */
689 int i;
690 for (i = 0; i < mix_infos_ctr; i++)
691 {
692 mix_info *md;
693 md = mix_infos[i];
694 if ((md) && (md->cp == cp))
695 mix_infos[i] = free_mix_info(md);
696 }
697 }
698
699
reset_mix_ctr(void)700 void reset_mix_ctr(void)
701 {
702 mix_infos_ctr = 0;
703 }
704
705
mix_name(int id)706 const char *mix_name(int id)
707 {
708 if (mix_exists(id))
709 return(mix_infos[id]->name);
710 return(NULL);
711 }
712
713
mix_file_name(int id)714 const char *mix_file_name(int id)
715 {
716 if (mix_exists(id))
717 return(mix_infos[id]->in_filename);
718 return(NULL);
719 }
720
721
mix_set_file_name(int id,int chans,const char * name)722 static void mix_set_file_name(int id, int chans, const char *name)
723 {
724 int i;
725
726 for (i = 0; i < chans; i++)
727 {
728 if (mix_exists(id + i))
729 {
730 mix_info *md;
731 md = md_from_id(id + i);
732 if (md->in_filename) free(md->in_filename);
733 md->in_filename = mus_strdup(name);
734 md->in_chan = i;
735 }
736 }
737 }
738
739
mix_name_to_id(const char * name)740 int mix_name_to_id(const char *name)
741 {
742 int i, loc_so_far = -1;
743 chan_info *selected_cp;
744 selected_cp = selected_channel();
745 for (i = 0; i < mix_infos_size; i++)
746 if ((mix_infos[i]) &&
747 (mus_strcmp(mix_infos[i]->name, name)))
748 {
749 if ((!selected_cp) ||
750 (mix_infos[i]->cp == selected_cp)) /* try to find mix in the currently selected channel (possible name collisions) */
751 return(i);
752 if (loc_so_far == -1)
753 loc_so_far = i;
754 }
755 return(loc_so_far);
756 }
757
758
make_mix_info(chan_info * cp)759 static mix_info *make_mix_info(chan_info *cp)
760 {
761 mix_info *md;
762 if (!mix_infos)
763 {
764 mix_infos_size = MIX_INFO_INCREMENT;
765 mix_infos = (mix_info **)calloc(mix_infos_size, sizeof(mix_info *));
766 }
767 else
768 {
769 if (mix_infos_ctr >= mix_infos_size)
770 {
771 int i;
772 mix_infos_size += MIX_INFO_INCREMENT;
773 mix_infos = (mix_info **)realloc(mix_infos, mix_infos_size * sizeof(mix_info *));
774 for (i = mix_infos_size - MIX_INFO_INCREMENT; i < mix_infos_size; i++)
775 mix_infos[i] = NULL;
776 }
777 }
778 md = (mix_info *)calloc(1, sizeof(mix_info));
779 mix_infos[mix_infos_ctr] = md;
780 md->id = mix_infos_ctr++;
781 md->cp = cp;
782 md->temporary = DONT_DELETE_ME;
783 md->color = ss->mix_color;
784 md->original_color = md->color;
785 md->tag_y = 0;
786 md->tag_x = 0;
787 md->name = NULL;
788 md->y = MIX_TAG_ERASED;
789 md->peak_env = NULL;
790 md->properties_gc_loc = NOT_A_GC_LOC;
791 md->properties = Xen_false;
792 md->sync = 0;
793 md->original_sync = ORIGINAL_SYNC_UNSET;
794 return(md);
795 }
796
797
prepare_mix_state_for_channel(chan_info * cp,int mix_loc,mus_long_t beg,mus_long_t len)798 mix_state *prepare_mix_state_for_channel(chan_info *cp, int mix_loc, mus_long_t beg, mus_long_t len)
799 {
800 mix_info *md;
801 md = make_mix_info(cp); /* make the mix_info data for this virtual mix */
802 md->original_index = mix_loc;
803 md->in_samps = len;
804 return(make_mix_state(md->id, mix_loc, beg, len));
805 }
806
807
for_each_channel_mix(chan_info * cp,void (* func)(mix_info * umx))808 static void for_each_channel_mix(chan_info *cp, void (*func)(mix_info *umx))
809 {
810 int i;
811 for (i = 0; i < mix_infos_ctr; i++)
812 {
813 mix_info *md;
814 md = mix_infos[i];
815 if ((md) && (md->cp == cp))
816 (*func)(md);
817 }
818 }
819
820
for_each_syncd_mix(int current_mix_id,void (* func)(mix_info * md,void * data),void * udata)821 static void for_each_syncd_mix(int current_mix_id, void (*func)(mix_info *md, void *data), void *udata)
822 {
823 int sync;
824 sync = mix_sync_from_id(current_mix_id);
825 if (sync != 0)
826 {
827 int i;
828 for (i = 0; i < mix_infos_ctr; i++)
829 if ((i != current_mix_id) &&
830 (mix_is_active(i)))
831 {
832 mix_info *md;
833 md = mix_infos[i];
834 if ((md) &&
835 (md->sync == sync))
836 (*func)(md, udata);
837 }
838 }
839 }
840
841
channel_has_mixes(chan_info * cp)842 bool channel_has_mixes(chan_info *cp)
843 {
844 int i;
845 for (i = 0; i < mix_infos_ctr; i++)
846 {
847 mix_info *md;
848 md = mix_infos[i];
849 if ((md) &&
850 (md->cp == cp))
851 return(true);
852 }
853 return(false);
854 }
855
856
channel_has_active_mixes(chan_info * cp)857 bool channel_has_active_mixes(chan_info *cp)
858 {
859 return((bool)(cp->edits[cp->edit_ctr]->mixes));
860 }
861
862
remove_temporary_mix_file(mix_info * md)863 static void remove_temporary_mix_file(mix_info *md)
864 {
865 if ((md->temporary == DELETE_ME) &&
866 (mus_file_probe(md->in_filename)))
867 snd_remove(md->in_filename, REMOVE_FROM_CACHE);
868 }
869
870
delete_any_remaining_mix_temp_files_at_exit(chan_info * cp)871 void delete_any_remaining_mix_temp_files_at_exit(chan_info *cp)
872 {
873 for_each_channel_mix(cp, remove_temporary_mix_file);
874 }
875
876
mix_info_to_file(FILE * fd,chan_info * cp)877 void mix_info_to_file(FILE *fd, chan_info *cp)
878 {
879 int i, n;
880 bool write_info = false;
881
882 for (i = 0, n = 0; i < mix_infos_ctr; i++)
883 {
884 mix_info *md;
885 md = mix_infos[i];
886 if ((md) && (md->cp == cp))
887 {
888 if ((!write_info) &&
889 ((md->sync > 0) ||
890 #if (!USE_NO_GUI)
891 (md->tag_y > 0) ||
892 (md->color != ss->mix_color) ||
893 #endif
894 (md->name)))
895 {
896 write_info = true;
897 fprintf(fd, " (let ((m (list->vector (mixes sfile %d))))\n"
898 " (when (> (length m) 0)",
899 cp->chan);
900 }
901 if (md->sync > 0)
902 fprintf(fd, "\n (set! (mix-sync (m %d)) %d)", n, md->sync);
903 if (md->name)
904 fprintf(fd, "\n (set! (mix-name (m %d)) \"%s\")", n, md->name);
905 #if (!USE_NO_GUI)
906 if (md->tag_y > 0)
907 fprintf(fd, "\n (set! (mix-tag-y (m %d)) %d)", n, md->tag_y);
908 if (md->color != ss->mix_color)
909 {
910 double r, g, b;
911 #if USE_MOTIF
912 XColor tmp_color;
913 Display *dpy;
914 dpy = XtDisplay(main_shell(ss));
915 tmp_color.flags = DoRed | DoGreen | DoBlue;
916 tmp_color.pixel = md->color;
917 XQueryColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), &tmp_color);
918 r = rgb_to_float(tmp_color.red);
919 g = rgb_to_float(tmp_color.green);
920 b = rgb_to_float(tmp_color.blue);
921 #endif
922 fprintf(fd, "\n (set! (mix-color (m %d)) (make-color %f %f %f))",
923 n, r, g, b);
924 }
925 #endif
926 n++;
927 }
928 }
929 if (write_info) fprintf(fd, "))\n");
930 }
931
932
compare_mix_positions(const void * umx1,const void * umx2)933 static int compare_mix_positions(const void *umx1, const void *umx2)
934 {
935 mus_long_t mx1, mx2;
936 mx1 = (*((mus_long_t *)umx1));
937 mx2 = (*((mus_long_t *)umx2));
938 if (mx1 > mx2) return(1);
939 if (mx1 == mx2) return(0);
940 return(-1);
941 }
942
943
goto_mix(chan_info * cp,int count)944 void goto_mix(chan_info *cp, int count)
945 {
946 /* C-x C-j */
947 mix_list *mxl;
948 if (!cp) return;
949 mxl = (mix_list *)(cp->edits[cp->edit_ctr]->mixes); /* active mixes in the current edit of this channel */
950 if (mxl)
951 {
952 int i, k = 0;
953 for (i = 0; i < mxl->size; i++)
954 if (mxl->list[i])
955 k++;
956 if (k > 0)
957 {
958 int j = 0;
959 mus_long_t *begs;
960 begs = (mus_long_t *)calloc(k, sizeof(mus_long_t));
961 for (i = 0; i < mxl->size; i++)
962 if (mxl->list[i])
963 begs[j++] = mxl->list[i]->beg;
964 if (k == 1)
965 cursor_moveto(cp, begs[0]);
966 else
967 {
968 qsort((void *)begs, j, sizeof(mus_long_t), compare_mix_positions);
969 /* now find where we are via cursor_sample(cp) and go forward or back as per count */
970 if (count > 0)
971 {
972 for (i = 0; i < j; i++)
973 if (begs[i] > cursor_sample(cp))
974 {
975 count--;
976 if (count == 0)
977 {
978 cursor_moveto(cp, begs[i]);
979 break;
980 }
981 }
982 if ((count > 0) && (cursor_sample(cp) < begs[j - 1]))
983 cursor_moveto(cp, begs[j - 1]);
984 }
985 else
986 {
987 for (i = j - 1; i >= 0; i--)
988 if (begs[i] < cursor_sample(cp))
989 {
990 count++;
991 if (count == 0)
992 {
993 cursor_moveto(cp, begs[i]);
994 break;
995 }
996 }
997 if ((count < 0) && (cursor_sample(cp) > begs[0]))
998 cursor_moveto(cp, begs[0]);
999 }
1000 }
1001 free(begs);
1002 }
1003 }
1004 }
1005
1006
zoom_focus_mix_in_channel_to_position(chan_info * cp)1007 mus_long_t zoom_focus_mix_in_channel_to_position(chan_info *cp)
1008 {
1009 mix_list *mxl;
1010 mxl = (mix_list *)(cp->edits[cp->edit_ctr]->mixes);
1011 if (mxl)
1012 {
1013 mus_long_t lo, hi;
1014 int i;
1015 lo = cp->axis->losamp;
1016 hi = cp->axis->hisamp;
1017 for (i = 0; i < mxl->size; i++)
1018 {
1019 mix_state *ms;
1020 ms = mxl->list[i];
1021 if ((ms) &&
1022 (ms->beg >= lo) &&
1023 (ms->beg <= hi))
1024 return(ms->beg);
1025 }
1026 }
1027 return(-1);
1028 }
1029
1030
1031 /* follow edit list */
mix_position_from_id(int id)1032 mus_long_t mix_position_from_id(int id)
1033 {
1034 mix_state *ms;
1035 ms = current_mix_state(md_from_id(id));
1036 if (ms)
1037 return(ms->beg);
1038 return(0);
1039 }
1040
1041
mix_length_from_id(int id)1042 mus_long_t mix_length_from_id(int id)
1043 {
1044 mix_state *ms;
1045 ms = current_mix_state(md_from_id(id));
1046 if (ms)
1047 return(ms->len);
1048 return(0);
1049 }
1050
1051
mix_amp_from_id(int id)1052 mus_float_t mix_amp_from_id(int id)
1053 {
1054 mix_state *ms;
1055 ms = current_mix_state(md_from_id(id));
1056 if (ms)
1057 return(ms->scaler);
1058 return(0.0);
1059 }
1060
1061
mix_speed_from_id(int id)1062 mus_float_t mix_speed_from_id(int id)
1063 {
1064 mix_state *ms;
1065 ms = current_mix_state(md_from_id(id));
1066 if (ms)
1067 return(ms->speed);
1068 return(0.0);
1069 }
1070
1071
mix_amp_env_from_id(int id)1072 env *mix_amp_env_from_id(int id)
1073 {
1074 mix_state *ms;
1075 ms = current_mix_state(md_from_id(id));
1076 if (ms)
1077 return(ms->amp_env);
1078 return(NULL);
1079 }
1080
1081
1082 /* stable (not in edit list) */
1083
mix_sync_from_id(int id)1084 int mix_sync_from_id(int id)
1085 {
1086 mix_info *md;
1087 md = md_from_id(id);
1088 if (md)
1089 return(md->sync);
1090 return(0);
1091 }
1092
1093
1094 static int current_mix_sync_max = 0;
1095
mix_set_sync_from_id(int id,int new_sync)1096 int mix_set_sync_from_id(int id, int new_sync)
1097 {
1098 mix_info *md;
1099 md = md_from_id(id);
1100 if (md)
1101 {
1102 if (new_sync == GET_NEW_SYNC)
1103 {
1104 new_sync = current_mix_sync_max + 1;
1105 md->original_sync = new_sync;
1106 }
1107 else
1108 {
1109 if (new_sync == GET_ORIGINAL_SYNC)
1110 {
1111 if (md->original_sync == ORIGINAL_SYNC_UNSET)
1112 new_sync = current_mix_sync_max + 1;
1113 else new_sync = md->original_sync;
1114 }
1115 else
1116 {
1117 if (new_sync != 0)
1118 md->original_sync = new_sync;
1119 }
1120 }
1121
1122 md->sync = new_sync;
1123 if (new_sync > current_mix_sync_max)
1124 current_mix_sync_max = new_sync;
1125 return(md->sync);
1126 }
1127 return(0);
1128 }
1129
1130
mix_name_from_id(int id)1131 static const char *mix_name_from_id(int id)
1132 {
1133 mix_info *md;
1134 md = md_from_id(id);
1135 if (md)
1136 return(md->name);
1137 return(NULL);
1138 }
1139
1140
mix_set_name_from_id(int id,const char * new_name)1141 static const char *mix_set_name_from_id(int id, const char *new_name)
1142 {
1143 mix_info *md;
1144 md = md_from_id(id);
1145 if (md)
1146 {
1147 if (md->name) free(md->name);
1148 md->name = mus_strdup(new_name);
1149 return(new_name);
1150 }
1151 return(NULL);
1152 }
1153
1154
mix_chan_info_from_id(int id)1155 chan_info *mix_chan_info_from_id(int id)
1156 {
1157 mix_info *md;
1158 md = md_from_id(id);
1159 if (md)
1160 return(md->cp);
1161 return(NULL);
1162 }
1163
1164
mix_tag_y_from_id(int id)1165 static int mix_tag_y_from_id(int id)
1166 {
1167 mix_info *md;
1168 md = md_from_id(id);
1169 if (md)
1170 return(md->tag_y);
1171 return(0);
1172 }
1173
1174
mix_color_from_id(int mix_id)1175 color_t mix_color_from_id(int mix_id)
1176 {
1177 mix_info *md;
1178 md = md_from_id(mix_id);
1179 if (md)
1180 return(md->color);
1181 return(ss->mix_color);
1182 }
1183
1184
mix_set_color_from_id(int id,color_t new_color)1185 color_t mix_set_color_from_id(int id, color_t new_color)
1186 {
1187 mix_info *md;
1188 md = md_from_id(id);
1189 if (md)
1190 md->color = new_color;
1191 return(new_color);
1192 }
1193
1194
mix_unset_color_from_id(int id)1195 void mix_unset_color_from_id(int id)
1196 {
1197 mix_info *md;
1198 md = md_from_id(id);
1199 if (md)
1200 md->color = md->original_color;
1201 }
1202
1203
syncd_mix_unset_color_1(mix_info * md,void * ignore)1204 static void syncd_mix_unset_color_1(mix_info *md, void *ignore)
1205 {
1206 md->color = md->original_color;
1207 }
1208
1209
syncd_mix_unset_color(int id)1210 void syncd_mix_unset_color(int id)
1211 {
1212 for_each_syncd_mix(id, syncd_mix_unset_color_1, NULL);
1213 }
1214
1215
syncd_mix_set_color_1(mix_info * md,void * ignore)1216 static void syncd_mix_set_color_1(mix_info *md, void *ignore)
1217 {
1218 /* assume red (this is from the mix dialog) */
1219 md->color = ss->red;
1220 }
1221
1222
syncd_mix_set_color(int id,color_t col)1223 void syncd_mix_set_color(int id, color_t col)
1224 {
1225 for_each_syncd_mix(id, syncd_mix_set_color_1, NULL);
1226 }
1227
1228
mix_set_amp_edit(int id,mus_float_t amp)1229 bool mix_set_amp_edit(int id, mus_float_t amp)
1230 {
1231 mix_info *md;
1232 bool edited = false;
1233 mix_state *old_ms = NULL;
1234 md = md_from_id(id);
1235 if (md) old_ms = current_mix_state(md); /* needed for edit bounds and existence check */
1236 if (old_ms)
1237 {
1238 if (old_ms->scaler != amp)
1239 {
1240 char *origin = NULL;
1241 #if HAVE_FORTH
1242 origin = mus_format("-mix-%d %.4f set-mix-amp", id, amp);
1243 #endif
1244 #if HAVE_SCHEME
1245 origin = mus_format("(set! (mix-amp (car -mix-%d)) %.4f)", id, amp);
1246 #endif
1247 #if HAVE_RUBY
1248 origin = mus_format("set_mix_amp(_mix_%d, %.4f)", id, amp);
1249 #endif
1250 edited = begin_mix_op(md->cp, old_ms->beg, old_ms->len, old_ms->beg, old_ms->len, md->cp->edit_ctr, origin);
1251 free(origin);
1252 if (edited)
1253 {
1254 mix_state *ms;
1255 ms = current_mix_state(md); /* this is the new copy reflecting this edit */
1256 ms->scaler = amp;
1257 end_mix_op(md->cp, 0, 0);
1258 }
1259 }
1260 }
1261 return(edited);
1262 }
1263
1264
1265 typedef struct {mus_float_t amp;} syncd_amp_info;
1266
syncd_mix_set_amp_1(mix_info * md,void * amp)1267 static void syncd_mix_set_amp_1(mix_info *md, void *amp)
1268 {
1269 mus_long_t beg, len;
1270 syncd_amp_info *ai = (syncd_amp_info *)amp;
1271 mix_set_amp_edit(md->id, ai->amp);
1272 beg = mix_position_from_id(md->id);
1273 len = mix_length_from_id(md->id);
1274 mix_display_during_drag(md->id, beg, beg + len);
1275 }
1276
1277
syncd_mix_set_amp(int id,mus_float_t amp)1278 void syncd_mix_set_amp(int id, mus_float_t amp)
1279 {
1280 syncd_amp_info *ai;
1281 ai = (syncd_amp_info *)malloc(sizeof(syncd_amp_info));
1282 ai->amp = amp;
1283 for_each_syncd_mix(id, syncd_mix_set_amp_1, (void *)ai);
1284 free(ai);
1285 }
1286
1287
src_input(void * arg,int direction)1288 static mus_float_t src_input(void *arg, int direction)
1289 {
1290 return(read_sample((snd_fd *)arg));
1291 }
1292
1293
remake_mix_data(mix_state * ms,mix_info * md)1294 static int remake_mix_data(mix_state *ms, mix_info *md)
1295 {
1296 chan_info *cp;
1297 mus_long_t len;
1298 snd_fd *mix_reader;
1299 mus_float_t old_amp;
1300 mus_any *egen = NULL, *src_gen = NULL;
1301 env *e;
1302
1303 cp = md->cp;
1304 old_amp = ms->scaler;
1305 ms->scaler = 1.0;
1306 len = snd_round_mus_long_t((double)(md->in_samps) / (double)(ms->speed));
1307 e = ms->amp_env;
1308
1309 mix_reader = make_virtual_mix_reader(cp, 0, md->in_samps, md->original_index, 1.0, READ_FORWARD);
1310
1311 if (e)
1312 egen = mus_make_env(e->data, e->pts, 1.0, 0.0, 1.0, 0.0, len - 1, NULL);
1313 if (ms->speed != 1.0)
1314 src_gen = mus_make_src(&src_input, ms->speed, sinc_width(ss), (void *)mix_reader);
1315
1316 prepare_sound_list(cp);
1317 if (cp->sounds[md->original_index]->type == SND_DATA_BUFFER)
1318 {
1319 int i;
1320 mus_float_t *new_buffer;
1321 new_buffer = (mus_float_t *)malloc(len * sizeof(mus_float_t));
1322 if (!src_gen)
1323 {
1324 for (i = 0; i < len; i++)
1325 new_buffer[i] = (mus_env(egen) * read_sample(mix_reader));
1326 }
1327 else
1328 {
1329 if (!egen)
1330 {
1331 for (i = 0; i < len; i++)
1332 new_buffer[i] = (mus_src(src_gen, 0.0, &src_input));
1333 }
1334 else
1335 {
1336 for (i = 0; i < len; i++)
1337 new_buffer[i] = (mus_src(src_gen, 0.0, &src_input) * mus_env(egen));
1338 }
1339 }
1340 cp->sounds[cp->sound_ctr] = make_snd_data_buffer(new_buffer, (int)len, cp->edit_ctr);
1341 free(new_buffer);
1342 }
1343 else
1344 {
1345 mus_long_t i;
1346 file_info *hdr;
1347 int fd, err = 0;
1348 char *temp_file;
1349 io_error_t io_err = IO_NO_ERROR;
1350 mus_float_t **data;
1351 mus_float_t *new_buffer;
1352 int j = 0;
1353
1354 temp_file = snd_tempnam();
1355 hdr = make_temp_header(temp_file, snd_srate(cp->sound), 1, len, S_set S_mix_amp_env);
1356 fd = open_temp_file(temp_file, 1, hdr, &io_err);
1357 data = (mus_float_t **)malloc(sizeof(mus_float_t *));
1358 new_buffer = (mus_float_t *)malloc(MAX_BUFFER_SIZE * sizeof(mus_float_t));
1359 data[0] = new_buffer;
1360
1361 if (!src_gen)
1362 {
1363 for (i = 0; i < len; i++)
1364 {
1365 new_buffer[j++] = (read_sample(mix_reader) * mus_env(egen));
1366 if (j == MAX_BUFFER_SIZE)
1367 {
1368 err = mus_file_write(fd, 0, j - 1, 1, data);
1369 j = 0;
1370 if (err != MUS_NO_ERROR) break;
1371 }
1372 }
1373 }
1374 else
1375 {
1376 if (!egen)
1377 {
1378 for (i = 0; i < len; i++)
1379 {
1380 new_buffer[j++] = (mus_src(src_gen, 0.0, &src_input));
1381 if (j == MAX_BUFFER_SIZE)
1382 {
1383 err = mus_file_write(fd, 0, j - 1, 1, data);
1384 j = 0;
1385 if (err != MUS_NO_ERROR) break;
1386 }
1387 }
1388 }
1389 else
1390 {
1391 for (i = 0; i < len; i++)
1392 {
1393 new_buffer[j++] = (mus_src(src_gen, 0.0, &src_input) * mus_env(egen));
1394 if (j == MAX_BUFFER_SIZE)
1395 {
1396 err = mus_file_write(fd, 0, j - 1, 1, data);
1397 j = 0;
1398 if (err != MUS_NO_ERROR) break;
1399 }
1400 }
1401 }
1402 }
1403 if (j > 0) mus_file_write(fd, 0, j - 1, 1, data);
1404 close_temp_file(temp_file, fd, hdr->type, len * mus_bytes_per_sample(hdr->sample_type));
1405 free_file_info(hdr);
1406
1407 hdr = make_file_info(temp_file, FILE_READ_ONLY, FILE_NOT_SELECTED);
1408 fd = snd_open_read(temp_file);
1409 snd_file_open_descriptors(fd,
1410 temp_file,
1411 hdr->sample_type,
1412 hdr->data_location,
1413 hdr->chans,
1414 hdr->type);
1415 cp->sounds[cp->sound_ctr] = make_snd_data_file(temp_file,
1416 make_file_state(fd, hdr, 0, 0, FILE_BUFFER_SIZE),
1417 hdr, DELETE_ME, cp->edit_ctr, 0);
1418 free(temp_file);
1419 free(new_buffer);
1420 free(data);
1421 }
1422
1423 free_snd_fd(mix_reader);
1424 if (egen) mus_free(egen);
1425 if (src_gen) mus_free(src_gen);
1426
1427 ms->scaler = old_amp;
1428 return(cp->sound_ctr);
1429 }
1430
1431
mix_set_amp_env_edit(int id,env * e)1432 bool mix_set_amp_env_edit(int id, env *e)
1433 {
1434 mix_info *md;
1435 bool edited = false;
1436 mix_state *old_ms = NULL;
1437 md = md_from_id(id);
1438 if (md) old_ms = current_mix_state(md); /* needed for edit bounds and existence check */
1439 if (old_ms)
1440 {
1441 if (!(envs_equal(old_ms->amp_env, e)))
1442 {
1443 chan_info *cp;
1444 char *origin = NULL, *envstr;
1445
1446 envstr = env_to_string(e);
1447 #if HAVE_FORTH
1448 origin = mus_format("-mix-%d %s set-mix-amp-env", id, envstr);
1449 #endif
1450 #if HAVE_SCHEME
1451 origin = mus_format("(set! (mix-amp-env (car -mix-%d)) %s)", id, envstr);
1452 #endif
1453 #if HAVE_RUBY
1454 origin = mus_format("set_mix_amp_env(_mix_%d, %s)", id, envstr);
1455 #endif
1456 free(envstr);
1457
1458 cp = md->cp;
1459 edited = begin_mix_op(cp, old_ms->beg, old_ms->len, old_ms->beg, old_ms->len, cp->edit_ctr, origin); /* this does not change beg or len */
1460 free(origin);
1461 if (edited)
1462 {
1463 mix_state *ms;
1464 ms = current_mix_state(md); /* this is the new copy reflecting this edit */
1465 if (ms->amp_env) free_env(ms->amp_env);
1466 ms->amp_env = copy_env(e);
1467
1468 /* can't use mus_env (as the reader op) here because we need to run backwards */
1469 if ((e) || (ms->speed != 1.0))
1470 ms->index = remake_mix_data(ms, md);
1471 else ms->index = md->original_index;
1472
1473 end_mix_op(cp, 0, 0);
1474 }
1475 }
1476 }
1477 return(edited);
1478 }
1479
1480
syncd_mix_set_amp_env_1(mix_info * md,void * e)1481 static void syncd_mix_set_amp_env_1(mix_info *md, void *e)
1482 {
1483 mus_long_t beg, len;
1484 env *amp_env = (env *)e;
1485 mix_set_amp_env_edit(md->id, amp_env);
1486 beg = mix_position_from_id(md->id);
1487 len = mix_length_from_id(md->id);
1488 mix_display_during_drag(md->id, beg, beg + len);
1489 }
1490
1491
syncd_mix_set_amp_env(int id,env * e)1492 void syncd_mix_set_amp_env(int id, env *e)
1493 {
1494 for_each_syncd_mix(id, syncd_mix_set_amp_env_1, (void *)e);
1495 }
1496
1497
1498
mix_set_position_edit(int id,mus_long_t pos)1499 bool mix_set_position_edit(int id, mus_long_t pos)
1500 {
1501 mix_info *md;
1502 bool edited = false;
1503 mix_state *old_ms = NULL;
1504 if (pos < 0) pos = 0;
1505 md = md_from_id(id);
1506 if (md) old_ms = current_mix_state(md);
1507 if (old_ms)
1508 {
1509 if (old_ms->beg != pos)
1510 {
1511 char *origin = NULL;
1512 #if HAVE_FORTH
1513 origin = mus_format("-mix-%d %" print_mus_long " set-mix-position", id, pos);
1514 #endif
1515 #if HAVE_SCHEME
1516 origin = mus_format("(set! (mix-position (car -mix-%d)) %" print_mus_long ")", id, pos);
1517 #endif
1518 #if HAVE_RUBY
1519 origin = mus_format("set_mix_position(_mix_%d, %" print_mus_long ")", id, pos);
1520 #endif
1521 edited = begin_mix_op(md->cp, old_ms->beg, old_ms->len, pos, old_ms->len, md->cp->edit_ctr, origin); /* this does not change beg or len */
1522
1523 free(origin);
1524 if (edited)
1525 {
1526 mix_state *ms;
1527 ms = current_mix_state(md); /* this is the new copy reflecting this edit */
1528 unmix(md->cp, ms);
1529 ms->beg = pos;
1530 remix(md->cp, ms);
1531 end_mix_op(md->cp, (old_ms->beg != pos) ? old_ms->beg : 0, old_ms->len);
1532 }
1533 }
1534 }
1535 return(edited);
1536 }
1537
1538
mix_set_speed_edit(int id,mus_float_t spd)1539 bool mix_set_speed_edit(int id, mus_float_t spd)
1540 {
1541 mix_info *md;
1542 bool edited = false;
1543 mix_state *old_ms = NULL;
1544 md = md_from_id(id);
1545 if (md) old_ms = current_mix_state(md); /* needed for edit bounds and existence check */
1546 if (old_ms)
1547 {
1548 if (old_ms->speed != spd)
1549 {
1550 chan_info *cp;
1551 mus_long_t len;
1552 char *origin = NULL;
1553 #if HAVE_FORTH
1554 origin = mus_format("-mix-%d %.4f set-mix-speed", id, spd);
1555 #endif
1556 #if HAVE_SCHEME
1557 origin = mus_format("(set! (mix-speed (car -mix-%d)) %.4f)", id, spd);
1558 #endif
1559 #if HAVE_RUBY
1560 origin = mus_format("set_mix_speed(_mix_%d, %.4f)", id, spd);
1561 #endif
1562 cp = md->cp;
1563 len = snd_round_mus_long_t((double)(md->in_samps) / (double)spd);
1564 edited = begin_mix_op(cp, old_ms->beg, old_ms->len, old_ms->beg, len, cp->edit_ctr, origin);
1565
1566 free(origin);
1567 if (edited)
1568 {
1569 mix_state *ms;
1570 ms = current_mix_state(md); /* this is the new copy reflecting this edit */
1571 unmix(cp, ms); /* but unmix before changing mix length! */
1572
1573 ms->speed = spd;
1574 ms->len = len;
1575 if ((ms->speed != 1.0) || (ms->amp_env))
1576 ms->index = remake_mix_data(ms, md);
1577 else ms->index = md->original_index;
1578
1579 remix(cp, ms);
1580 end_mix_op(cp, 0, 0); /* old_ms->beg, old_ms->len); */
1581 }
1582 }
1583 }
1584 return(edited);
1585 }
1586
1587
1588 typedef struct {mus_float_t speed;} syncd_speed_info;
1589
syncd_mix_set_speed_1(mix_info * md,void * speed)1590 static void syncd_mix_set_speed_1(mix_info *md, void *speed)
1591 {
1592 mus_long_t beg, len;
1593 syncd_speed_info *ai = (syncd_speed_info *)speed;
1594 mix_set_speed_edit(md->id, ai->speed);
1595 beg = mix_position_from_id(md->id);
1596 len = mix_length_from_id(md->id);
1597 mix_display_during_drag(md->id, beg, beg + len);
1598 }
1599
1600
syncd_mix_set_speed(int id,mus_float_t speed)1601 void syncd_mix_set_speed(int id, mus_float_t speed)
1602 {
1603 syncd_speed_info *ai;
1604 ai = (syncd_speed_info *)malloc(sizeof(syncd_speed_info));
1605 ai->speed = speed;
1606 for_each_syncd_mix(id, syncd_mix_set_speed_1, (void *)ai);
1607 free(ai);
1608 }
1609
1610
1611
1612
1613
1614 /* mix-samples/set-mix-samples?
1615 * combine mix_set_amp_env_edit with remake_mix_data accepting either vct or filename
1616 * one problem is how to handle md->peak_env: currently I think it is based on the original
1617 * and src stretches/mix-env uses env-on-env, so it's never remade.
1618 */
1619
1620
1621 /* edit-list->function support for mixes:
1622 * mix list search for current channel, make outer let holding all names as (-mix-### ###)
1623 * origins for make-mix procs: (set! -mix-### (...))
1624 * origins otherwise, reference to mix: -mix-###
1625 */
1626
edit_list_mix_init(chan_info * cp)1627 char *edit_list_mix_init(chan_info *cp)
1628 {
1629 char *new_list = NULL;
1630 mix_list *mxl;
1631 mxl = (mix_list *)(cp->edits[cp->edit_ctr]->mixes);
1632 if (mxl)
1633 {
1634 int i;
1635 for (i = 0; i < mxl->size; i++)
1636 if (mxl->list[i])
1637 {
1638 char *old_list;
1639 int id;
1640 id = mxl->list[i]->mix_id;
1641
1642 old_list = new_list;
1643 #if HAVE_SCHEME
1644 new_list = mus_format("%s%s(-mix-%d #f)",
1645 (old_list) ? old_list : "",
1646 (old_list) ? " " : "", /* strcat of previous + possible space */
1647 id);
1648 #endif
1649 #if HAVE_RUBY
1650 new_list = mus_format("%s%s_mix_%d = false",
1651 (old_list) ? old_list : "",
1652 (old_list) ? "; " : "", /* strcat of previous + possible space */
1653 id);
1654 #endif
1655 #if HAVE_FORTH
1656 new_list = mus_format("%s%s#f { -mix-%d }",
1657 (old_list) ? old_list : "",
1658 (old_list) ? " " : "", /* strcat of previous + possible space */
1659 id);
1660 #endif
1661 if (old_list) free(old_list);
1662 }
1663 }
1664 return(new_list);
1665 }
1666
1667
1668 #define MIX_TAG_Y_OFFSET mix_tag_height(ss)
1669
hit_mix(chan_info * cp,int x,int y)1670 int hit_mix(chan_info *cp, int x, int y) /* mix tag press in snd-chn.c */
1671 {
1672 #define SLOPPY_MOUSE 3
1673 mix_list *mxl;
1674 mxl = (mix_list *)(cp->edits[cp->edit_ctr]->mixes); /* active mixes in the current edit of this channel */
1675 if (mxl)
1676 {
1677 int i, width, height;
1678 width = mix_tag_width(ss);
1679 height = mix_tag_height(ss);
1680 for (i = 0; i < mxl->size; i++)
1681 {
1682 mix_state *ms;
1683 ms = mxl->list[i];
1684 if (ms)
1685 {
1686 int mx, my;
1687 mx = mix_infos[ms->mix_id]->tag_x;
1688 if (mx <= 0)
1689 mx = grf_x((double)(ms->beg) / (double)(snd_srate(cp->sound)), cp->axis);
1690 my = mix_infos[ms->mix_id]->tag_y + MIX_TAG_Y_OFFSET + cp->axis->y_offset;
1691 if ((x + SLOPPY_MOUSE >= (mx - width / 2)) &&
1692 (x - SLOPPY_MOUSE <= (mx + width / 2)) &&
1693 (y + SLOPPY_MOUSE >= (my - height)) &&
1694 (y - SLOPPY_MOUSE <= (my + 0)))
1695 return(ms->mix_id);
1696 }
1697 }
1698 }
1699 return(NO_MIX_TAG);
1700 }
1701
1702
1703 #define STRING_Y_OFFSET 3
1704 #define STRING_HEIGHT 12
1705 #define HIT_SLOP 4
1706
hit_mix_triangle(chan_info * cp,int x,int y)1707 int hit_mix_triangle(chan_info *cp, int x, int y)
1708 {
1709 mix_list *mxl;
1710 mxl = (mix_list *)(cp->edits[cp->edit_ctr]->mixes); /* active mixes in the current edit of this channel */
1711 if (mxl)
1712 {
1713 int i;
1714 for (i = 0; i < mxl->size; i++)
1715 {
1716 mix_state *ms;
1717 ms = mxl->list[i];
1718 if (ms)
1719 {
1720 int mx, my;
1721 mx = mix_infos[ms->mix_id]->tag_x;
1722 if (mx <= 0)
1723 mx = grf_x((double)(ms->beg) / (double)(snd_srate(cp->sound)), cp->axis);
1724 my = mix_infos[ms->mix_id]->tag_y + MIX_TAG_Y_OFFSET + STRING_HEIGHT + cp->axis->y_offset;
1725 if ((mx < (x + HIT_SLOP)) &&
1726 ((mx + play_arrow_size(ss) + HIT_SLOP) >= x) &&
1727 ((y + HIT_SLOP) > my) &&
1728 (y < (my + 2 * play_arrow_size(ss) + HIT_SLOP)))
1729 return(ms->mix_id);
1730 }
1731 }
1732 }
1733 return(NO_MIX_TAG);
1734 }
1735
1736
1737 /* mix display */
1738
channel_set_mix_tags_erased(chan_info * cp)1739 void channel_set_mix_tags_erased(chan_info *cp)
1740 {
1741 mix_list *mxl;
1742 mxl = (mix_list *)(cp->edits[cp->edit_ctr]->mixes);
1743 if (mxl)
1744 {
1745 int i;
1746 for (i = 0; i < mxl->size; i++)
1747 if (mxl->list[i])
1748 {
1749 mix_info *md;
1750 md = mix_infos[mxl->list[i]->mix_id];
1751 md->y = MIX_TAG_ERASED;
1752 }
1753 }
1754 }
1755
1756
1757 static Xen draw_mix_hook;
1758
draw_mix_tag(mix_info * md,int x,int y)1759 static void draw_mix_tag(mix_info *md, int x, int y)
1760 {
1761 chan_info *cp;
1762 int width, height;
1763 graphics_context *ax;
1764 char *lab = NULL;
1765
1766 if (Xen_hook_has_list(draw_mix_hook))
1767 {
1768 Xen res;
1769 res = run_progn_hook(draw_mix_hook,
1770 Xen_list_5(new_xen_mix(md->id),
1771 C_int_to_Xen_integer(md->x),
1772 C_int_to_Xen_integer(md->y),
1773 C_int_to_Xen_integer(x),
1774 C_int_to_Xen_integer(y)),
1775 S_draw_mix_hook);
1776 if (!(Xen_is_false(res)))
1777 {
1778 md->x = x;
1779 md->y = y;
1780 if (Xen_is_list(res))
1781 {
1782 md->tag_x = Xen_integer_to_C_int(Xen_car(res));
1783 md->tag_y = Xen_integer_to_C_int(Xen_cadr(res));
1784 }
1785 return;
1786 }
1787 }
1788
1789 cp = md->cp;
1790
1791 /* draw the mix tag */
1792 width = mix_tag_width(ss);
1793 height = mix_tag_height(ss);
1794
1795 if (md->y != MIX_TAG_ERASED)
1796 {
1797 /* erase old tag and name */
1798 ax = erase_context(cp);
1799 fill_rectangle(ax, md->x - width / 2 - 1, md->y - height - 1, width + 2, height + STRING_HEIGHT);
1800 md->y = MIX_TAG_ERASED;
1801 }
1802
1803 md->x = x;
1804 md->y = y;
1805
1806 /* redraw the mix id */
1807 ax = copy_context(cp);
1808 set_tiny_numbers_font(cp, ax);
1809 if (cp->printing) ps_set_tiny_numbers_font();
1810
1811 if (md->name)
1812 lab = mus_strdup(md->name);
1813 else
1814 {
1815 lab = (char *)calloc(16, sizeof(char));
1816 snprintf(lab, 16, "%d", md->id);
1817 }
1818 draw_string(ax, x - width / 2, y - height / 2 + STRING_Y_OFFSET, lab, strlen(lab));
1819 if (cp->printing) ps_draw_string(cp->axis, x - width / 2, y - height / 2 + STRING_Y_OFFSET, lab);
1820
1821 if (lab) {free(lab); lab = NULL;}
1822
1823 ax = mix_waveform_context(cp);
1824 set_foreground_color(ax, md->color);
1825 fill_rectangle(ax, x - width / 2, y - height + STRING_HEIGHT, width, height);
1826
1827 /* now draw the play triangle below the tag */
1828 y += (height - 4);
1829 fill_polygon(ax, 4,
1830 x, y,
1831 x + play_arrow_size(ss), y + play_arrow_size(ss),
1832 x, y + 2 * play_arrow_size(ss),
1833 x, y);
1834 }
1835
1836
local_grf_x(double val,axis_info * ap)1837 static int local_grf_x(double val, axis_info *ap)
1838 {
1839 if (val >= ap->x1) return(ap->x_axis_x1);
1840 if (val <= ap->x0) return(ap->x_axis_x0);
1841 return((int)(ap->x_base + val * ap->x_scale));
1842 }
1843
1844
1845 #define MIX_PEAK_ENV_CUTOFF 20000
1846
make_mix_input_peak_env(mix_info * md)1847 static peak_env_info *make_mix_input_peak_env(mix_info *md)
1848 {
1849 mix_state *ms;
1850 ms = current_mix_state(md);
1851 if (ms->len >= MIX_PEAK_ENV_CUTOFF)
1852 {
1853 peak_env_info *ep;
1854 snd_fd *sf;
1855 int val, sb = 0;
1856 mus_long_t n;
1857
1858 ep = (peak_env_info *)calloc(1, sizeof(peak_env_info));
1859 val = (int)(log((double)(ms->len)));
1860 if (val > 20) val = 20;
1861 ep->peak_env_size = snd_int_pow2(val);
1862 ep->samps_per_bin = (int)(ceil((double)(ms->len) / (double)(ep->peak_env_size)));
1863 ep->data_max = (mus_float_t *)calloc(ep->peak_env_size, sizeof(mus_float_t));
1864 ep->data_min = (mus_float_t *)calloc(ep->peak_env_size, sizeof(mus_float_t));
1865 ep->fmin = MIN_INIT;
1866 ep->fmax = MAX_INIT;
1867
1868 sf = make_virtual_mix_reader(md->cp, 0, ms->len, ms->index, 1.0, READ_FORWARD);
1869
1870 for (n = 0; n < ms->len; n += ep->samps_per_bin)
1871 {
1872 mus_float_t ymin, ymax, val;
1873 int i;
1874 val = read_sample(sf);
1875 ymin = val;
1876 ymax = val;
1877 for (i = 1; i < ep->samps_per_bin; i++)
1878 {
1879 val = read_sample(sf);
1880 if (ymin > val)
1881 ymin = val;
1882 else
1883 if (ymax < val)
1884 ymax = val;
1885 }
1886 ep->data_max[sb] = ymax;
1887 ep->data_min[sb++] = ymin;
1888 if (ymin < ep->fmin) ep->fmin = ymin;
1889 if (ymax > ep->fmax) ep->fmax = ymax;
1890 }
1891
1892 ep->completed = true;
1893 free_snd_fd(sf);
1894 return(ep);
1895 }
1896 return(NULL);
1897 }
1898
1899
mix_input_peak_env_usable(mix_info * md,mus_float_t samples_per_pixel)1900 static bool mix_input_peak_env_usable(mix_info *md, mus_float_t samples_per_pixel)
1901 {
1902 if (!(md->peak_env))
1903 md->peak_env = make_mix_input_peak_env(md);
1904 return((md->peak_env) &&
1905 (samples_per_pixel >= (mus_float_t)(md->peak_env->samps_per_bin)));
1906 }
1907
1908
env_on_env(env * e,peak_env_info * peaks)1909 static peak_env_info *env_on_env(env *e, peak_env_info *peaks)
1910 {
1911 peak_env_info *ep;
1912 ep = copy_peak_env_info(peaks, false);
1913 if (ep)
1914 {
1915 int i;
1916 mus_any *me;
1917 me = mus_make_env(e->data, e->pts, 1.0, 0.0, e->base, 0.0, ep->peak_env_size - 1, NULL);
1918 for (i = 0; i < ep->peak_env_size; i++)
1919 {
1920 mus_float_t val;
1921 val = mus_env(me);
1922 if (val >= 0.0)
1923 {
1924 ep->data_min[i] = ep->data_min[i] * val;
1925 ep->data_max[i] = ep->data_max[i] * val;
1926 }
1927 else
1928 {
1929 ep->data_min[i] = ep->data_max[i] * val;
1930 ep->data_max[i] = ep->data_min[i] * val;
1931 }
1932 }
1933 mus_free(me);
1934 }
1935 return(ep);
1936 }
1937
1938
prepare_mix_peak_env(mix_info * md,mus_float_t scl,int yoff,mus_long_t newbeg,mus_long_t newend,double srate,axis_info * ap)1939 static int prepare_mix_peak_env(mix_info *md, mus_float_t scl, int yoff, mus_long_t newbeg, mus_long_t newend, double srate, axis_info *ap)
1940 {
1941 int i, j, mix_start;
1942 int lastx;
1943 double xend, xstart, xstep, mix_samps_per_bin;
1944 peak_env_info *ep;
1945 env *amp_env;
1946 mus_float_t ymin = 0.0, ymax = 0.0;
1947
1948 amp_env = mix_amp_env_from_id(md->id);
1949 if (!amp_env)
1950 ep = md->peak_env;
1951 else ep = env_on_env(amp_env, md->peak_env);
1952
1953 mix_samps_per_bin = (double)(ep->samps_per_bin) / mix_speed_from_id(md->id);
1954
1955 /* mix starts at newbeg, current display starts at lo,
1956 mix goes to newend, current display goes to hi,
1957 */
1958
1959 if (ap->losamp > newbeg)
1960 {
1961 mix_start = snd_round((double)(ap->losamp - newbeg) / mix_samps_per_bin);
1962 xstart = ap->x0;
1963 }
1964 else
1965 {
1966 mix_start = 0;
1967 xstart = (double)(newbeg) / srate;
1968 }
1969
1970 if (ap->hisamp < newend)
1971 xend = ap->x1;
1972 else xend = (double)(newend) / srate;
1973
1974 xstep = mix_samps_per_bin / srate;
1975 lastx = local_grf_x(xstart, ap);
1976
1977 for (i = mix_start, j = 0; (xstart < xend) && (i < ep->peak_env_size); xstart += xstep, i++)
1978 {
1979 mus_float_t low, high;
1980 int newx;
1981 low = ep->data_min[i];
1982 high = ep->data_max[i];
1983 newx = local_grf_x(xstart, ap);
1984 if (newx > lastx) /* set lastx's bin (j) from min/max for that output bin */
1985 {
1986 set_grf_points(lastx, j++,
1987 (int)(yoff - scl * ymin),
1988 (int)(yoff - scl * ymax));
1989 if (j >= POINT_BUFFER_SIZE) break;
1990 lastx = newx;
1991 ymin = low;
1992 ymax = high;
1993 }
1994 else
1995 {
1996 if (high > ymax) ymax = high;
1997 if (low < ymin) ymin = low;
1998 }
1999 }
2000
2001 if (amp_env)
2002 free_peak_env_info(ep);
2003
2004 return(j);
2005 }
2006
2007
prepare_mix_waveform(mix_info * md,mix_state * ms,axis_info * ap,mus_float_t scl,int yoff,double cur_srate,bool * two_sided)2008 static int prepare_mix_waveform(mix_info *md, mix_state *ms, axis_info *ap, mus_float_t scl, int yoff, double cur_srate, bool *two_sided)
2009 {
2010 mus_long_t i, newbeg, newend;
2011 int pts = 0;
2012 mus_long_t samps;
2013 mus_float_t samples_per_pixel;
2014 double x;
2015 mus_long_t lo, hi;
2016 snd_fd *sf = NULL;
2017 int x_start, x_end;
2018 double start_time;
2019
2020 newbeg = ms->beg;
2021 newend = newbeg + ms->len;
2022 lo = ap->losamp;
2023 hi = ap->hisamp;
2024 if ((newend <= lo) || (newbeg >= hi)) return(0);
2025 if ((ap->y_axis_y0 - ap->y_axis_y1) < scl) return(0);
2026 start_time = (double)(ap->losamp) / cur_srate;
2027 x_start = ap->x_axis_x0;
2028 x_end = ap->x_axis_x1;
2029 if (newend > hi) newend = hi;
2030 samps = ap->hisamp - ap->losamp + 1;
2031 samples_per_pixel = (mus_float_t)((double)(samps - 1) / (mus_float_t)(x_end - x_start));
2032
2033 if ((samples_per_pixel < 1.0) ||
2034 ((samples_per_pixel < 5.0) &&
2035 (samps < POINT_BUFFER_SIZE)))
2036 {
2037 int j;
2038 bool widely_spaced;
2039 double incr, initial_x;
2040 if (newbeg < lo) /* mix starts before current left x0 point */
2041 {
2042 sf = make_virtual_mix_reader(md->cp, lo - newbeg, ms->len, ms->index, ms->scaler * scl, READ_FORWARD);
2043 newbeg = lo;
2044 }
2045 else sf = make_virtual_mix_reader(md->cp, 0, ms->len, ms->index, ms->scaler * scl, READ_FORWARD);
2046
2047 if (!sf) return(0);
2048 if (samples_per_pixel < 1.0)
2049 {
2050 incr = 1.0 / samples_per_pixel;
2051 initial_x = x_start;
2052 widely_spaced = true;
2053 }
2054 else
2055 {
2056 incr = (double)1.0 /cur_srate;
2057 initial_x = start_time;
2058 widely_spaced = false;
2059 }
2060 x = initial_x + (incr * (newbeg - lo));
2061 for (j = 0, i = newbeg; i <= newend; i++, j++, x += incr)
2062 {
2063 int ina_i;
2064 ina_i = (int)(yoff - read_sample(sf));
2065 if (widely_spaced)
2066 set_grf_point((int)x, j, ina_i);
2067 else set_grf_point(local_grf_x(x, ap), j, ina_i);
2068 }
2069 free_snd_fd(sf);
2070 pts = j;
2071 (*two_sided) = false;
2072 }
2073 else
2074 {
2075 (*two_sided) = true;
2076 if (mix_input_peak_env_usable(md, samples_per_pixel))
2077 pts = prepare_mix_peak_env(md, ms->scaler * scl, yoff, newbeg, newend, (double)cur_srate, ap);
2078 else
2079 {
2080 int xi, j;
2081 mus_long_t endi;
2082 mus_float_t ymin, ymax, xf;
2083 if (newbeg < lo)
2084 {
2085 sf = make_virtual_mix_reader(md->cp, lo - newbeg, ms->len, ms->index, ms->scaler * scl, READ_FORWARD);
2086 newbeg = lo;
2087 }
2088 else sf = make_virtual_mix_reader(md->cp, 0, ms->len, ms->index, ms->scaler * scl, READ_FORWARD);
2089 if (!sf) return(0);
2090
2091 j = 0; /* graph point counter */
2092 x = ap->x0;
2093 xi = local_grf_x(x, ap);
2094 xf = 0.0; /* samples per pixel counter */
2095 ymin = 100.0;
2096 ymax = -100.0;
2097 if (newend < hi) endi = newend; else endi = hi;
2098 for (i = lo; i < newbeg; i++)
2099 {
2100 xf += 1.0;
2101 if (xf > samples_per_pixel)
2102 {
2103 xi++;
2104 xf -= samples_per_pixel;
2105 }
2106 }
2107 for (i = newbeg; i <= endi; i++)
2108 {
2109 mus_float_t ina;
2110 ina = read_sample(sf);
2111 if (ina > ymax) ymax = ina;
2112 if (ina < ymin) ymin = ina;
2113 xf += 1.0;
2114 if (xf > samples_per_pixel)
2115 {
2116 set_grf_points(xi, j,
2117 (int)(yoff - ymin),
2118 (int)(yoff - ymax));
2119 j++;
2120 ymin = 100.0;
2121 ymax = -100.0;
2122 xi++;
2123 xf -= samples_per_pixel;
2124 }
2125 }
2126 pts = j;
2127 free_snd_fd(sf);
2128 }
2129 }
2130 return(pts);
2131 }
2132
2133
prepare_mix_dialog_waveform(int mix_id,axis_info * ap,bool * two_sided)2134 int prepare_mix_dialog_waveform(int mix_id, axis_info *ap, bool *two_sided)
2135 {
2136 mix_info *md;
2137 mix_state *ms;
2138 mus_float_t scl, x0, x1, y0, y1;
2139 mus_long_t old_lo, old_hi;
2140 double cur_srate;
2141 int pts;
2142
2143 md = md_from_id(mix_id);
2144 if (!md) return(0);
2145
2146 scl = ap->y_axis_y0 - ap->y_axis_y1;
2147 old_lo = ap->losamp;
2148 old_hi = ap->hisamp;
2149 x0 = ap->x0;
2150 x1 = ap->x1;
2151 y0 = ap->y0;
2152 y1 = ap->y1;
2153 cur_srate = (double)snd_srate(md->cp->sound);
2154 ms = current_mix_state(md);
2155 ap->losamp = ms->beg;
2156 ap->hisamp = ms->beg + ms->len;
2157 ap->x0 = (double)(ap->losamp) / cur_srate;
2158 ap->x1 = (double)(ap->hisamp) / cur_srate;
2159 ap->y0 = -1.0;
2160 ap->y1 = 1.0;
2161 init_axis_scales(ap);
2162
2163 pts = prepare_mix_waveform(md, ms, ap, scl * .5, (int)(scl * .5), cur_srate, two_sided);
2164
2165 ap->x0 = x0;
2166 ap->x1 = x1;
2167 ap->y0 = y0;
2168 ap->y1 = y1;
2169 ap->losamp = old_lo;
2170 ap->hisamp = old_hi;
2171 init_axis_scales(ap);
2172
2173 return(pts);
2174 }
2175
2176
erase_mix_tag_and_waveform(mix_state * ms,chan_info * cp,axis_info * ap,graphics_context * ax,int x,int y)2177 static void erase_mix_tag_and_waveform(mix_state *ms, chan_info *cp, axis_info *ap, graphics_context *ax, int x, int y)
2178 {
2179 int wave_width, wave_height, old_x;
2180 if (ap->hisamp > ap->losamp)
2181 wave_width = (int)(ms->len * ((double)(ap->x_axis_x1 - ap->x_axis_x0) / (double)(ap->hisamp - ap->losamp)));
2182 else wave_width = 0;
2183 wave_height = mix_waveform_height(ss);
2184 old_x = x + mix_tag_width(ss) - 2;
2185 if ((old_x + wave_width) > ap->x_axis_x1)
2186 wave_width = ap->x_axis_x1 - old_x;
2187 fill_rectangle(ax, old_x, y - wave_height + 6, wave_width + 2, wave_height + 12);
2188 }
2189
2190
draw_mix_tag_and_waveform(mix_info * md,mix_state * ms,int x)2191 static void draw_mix_tag_and_waveform(mix_info *md, mix_state *ms, int x)
2192 {
2193 bool show_wave;
2194 int y;
2195 chan_info *cp;
2196 axis_info *ap;
2197
2198 cp = md->cp;
2199 ap = cp->axis;
2200 show_wave = ((show_mix_waveforms(ss)) && (cp->show_mix_waveforms));
2201 y = ap->y_offset + md->tag_y + MIX_TAG_Y_OFFSET;
2202
2203 if (ms->beg >= ap->losamp)
2204 draw_mix_tag(md, x, y);
2205
2206 if (show_wave)
2207 {
2208 bool two_sided = false;
2209 int pts;
2210 pts = prepare_mix_waveform(md, ms, ap, mix_waveform_height(ss), y + STRING_HEIGHT / 2, (double)snd_srate(cp->sound), &two_sided);
2211 if (pts > 0)
2212 {
2213 graphics_context *ax;
2214 ax = mix_waveform_context(cp);
2215 if (two_sided)
2216 draw_both_grf_points(cp->dot_size, ax, pts, cp->time_graph_style);
2217 else draw_grf_points(cp->dot_size, ax, pts, ap, ungrf_y(ap, y), cp->time_graph_style);
2218 copy_context(cp);
2219 }
2220 }
2221 }
2222
2223
display_one_mix_with_bounds(mix_state * ms,chan_info * cp,axis_info * ap,mus_long_t beg,mus_long_t end)2224 static void display_one_mix_with_bounds(mix_state *ms, chan_info *cp, axis_info *ap, mus_long_t beg, mus_long_t end)
2225 {
2226 if ((ms) &&
2227 (ms->beg + ms->len > beg) &&
2228 (ms->beg < end))
2229 {
2230 mix_info *md;
2231 int x;
2232 md = mix_infos[ms->mix_id];
2233 x = grf_x((double)(ms->beg) / (double)snd_srate(cp->sound), ap);
2234 if ((x + mix_tag_width(ss)) <= ap->x_axis_x1) /* not cut off on right */
2235 draw_mix_tag_and_waveform(md, ms, x);
2236 }
2237 }
2238
2239
display_one_mix(mix_state * ms,chan_info * cp)2240 static void display_one_mix(mix_state *ms, chan_info *cp)
2241 {
2242 display_one_mix_with_bounds(ms, cp, cp->axis, cp->axis->losamp, cp->axis->hisamp);
2243 }
2244
2245
display_channel_mixes_with_bounds(chan_info * cp,mus_long_t beg,mus_long_t end)2246 static void display_channel_mixes_with_bounds(chan_info *cp, mus_long_t beg, mus_long_t end)
2247 {
2248 /* called in display_channel_data if cp has active mixes
2249 * it used to draw the tag and waveform if show_mix_waveforms(ss), but I think the tag should be drawn in any case
2250 */
2251 mix_list *mxl;
2252 if ((cp->sound->channel_style == CHANNELS_SUPERIMPOSED) ||
2253 (cp->squelch_update))
2254 return;
2255 mxl = (mix_list *)(cp->edits[cp->edit_ctr]->mixes); /* active mixes in the current edit of this channel */
2256 if (mxl)
2257 {
2258 int i;
2259 for (i = 0; i < mxl->size; i++)
2260 display_one_mix_with_bounds(mxl->list[i], cp, cp->axis, beg, end);
2261 }
2262 }
2263
2264
display_channel_mixes(chan_info * cp)2265 void display_channel_mixes(chan_info *cp)
2266 {
2267 display_channel_mixes_with_bounds(cp, cp->axis->losamp, cp->axis->hisamp);
2268 }
2269
2270
2271 #define MIX_WAIT_TIME 50
2272 static timeout_result_t watch_mix_proc = 0;
2273
stop_watch_mix_proc(void)2274 static void stop_watch_mix_proc(void)
2275 {
2276 if (watch_mix_proc != 0)
2277 {
2278 TIMEOUT_REMOVE(watch_mix_proc);
2279 watch_mix_proc = 0;
2280 }
2281 }
2282
2283
2284 static double watch_mix_x_incr = 1.0;
2285
2286 #if (!USE_NO_GUI)
watch_mix(TIMEOUT_ARGS)2287 static TIMEOUT_TYPE watch_mix(TIMEOUT_ARGS)
2288 {
2289 mix_info *md = (mix_info *)context;
2290 if (watch_mix_proc != 0)
2291 {
2292 watch_mix_x_incr *= 1.1;
2293 move_mix_tag(md->id, (int)(md->x + watch_mix_x_incr), md->y);
2294 watch_mix_proc = CALL_TIMEOUT(watch_mix, MIX_WAIT_TIME, md);
2295 }
2296 TIMEOUT_RESULT
2297 }
2298 #endif
2299
2300 static int edpos_before_drag = 0;
2301 static with_hook_t hookable_before_drag;
2302 static bool mix_dragged = false;
2303 static Xen mix_release_hook;
2304 static Xen mix_drag_hook;
2305 static mus_long_t orig_beg = 0;
2306 /* also mix_click_hook in snd-chn.c */
2307
2308 static mus_long_t drag_beg = 0, drag_end = 0;
2309
2310
2311 typedef struct {mus_long_t beg; bool axis_changed;} move_mix_data;
2312
2313 static mus_long_t syncd_mix_position(int id);
2314
move_syncd_mix(mix_info * md,void * data)2315 static void move_syncd_mix(mix_info *md, void *data)
2316 {
2317 move_mix_data *mmd = (move_mix_data *)data;
2318 mix_set_position_edit(md->id, syncd_mix_position(md->id) + mmd->beg);
2319 if (!mmd->axis_changed)
2320 mix_display_during_drag(md->id, drag_beg, drag_end);
2321 }
2322
2323
move_mix_tag(int mix_id,int x,int y)2324 void move_mix_tag(int mix_id, int x, int y)
2325 {
2326 /* dragging mix, hit_mix returns id, called only from snd-chn.c and above (watch_mix) */
2327 mix_info *md;
2328 mix_state *ms;
2329 axis_info *ap;
2330 chan_info *cp;
2331 bool axis_changed = false;
2332 mus_long_t pos;
2333
2334 md = md_from_id(mix_id);
2335 if (!md)
2336 {
2337 mix_dragged = false;
2338 if (watch_mix_proc != 0)
2339 {
2340 stop_watch_mix_proc();
2341 watch_mix_proc = 0;
2342 }
2343 return;
2344 }
2345 cp = md->cp;
2346
2347 if (!mix_dragged) /* starting to drag -- unmix now while we know the original position */
2348 {
2349 edpos_before_drag = cp->edit_ctr;
2350 hookable_before_drag = cp->hookable;
2351 cp->hookable = WITHOUT_HOOK;
2352 drag_beg = mix_position_from_id(mix_id);
2353 orig_beg = drag_beg;
2354 drag_end = drag_beg + mix_length_from_id(mix_id);
2355 start_dragging_syncd_mixes(mix_id);
2356 }
2357 else
2358 {
2359 cp->edit_ctr = edpos_before_drag;
2360 keep_dragging_syncd_mixes(mix_id);
2361 }
2362
2363 pos = snd_round_mus_long_t(ungrf_x(cp->axis, x) * (double)(snd_srate(cp->sound)));
2364 mix_set_position_edit(mix_id, pos);
2365
2366 mix_dragged = true;
2367 ap = cp->axis;
2368 ms = current_mix_state(md);
2369
2370 if ((x > ap->x_axis_x1) || (x < ap->x_axis_x0))
2371 {
2372 /* we're outside the graph */
2373 if (watch_mix_proc != 0)
2374 {
2375 if ((x < ap->x_axis_x0) && (ap->x0 == ap->xmin)) return;
2376 if ((x > ap->x_axis_x1) && (ap->x1 == ap->xmax)) return;
2377 }
2378 else
2379 {
2380 if (mix_dragged)
2381 {
2382 if (x < ap->x_axis_x0)
2383 watch_mix_x_incr = -1.0;
2384 else watch_mix_x_incr = 1.0;
2385 watch_mix_proc = CALL_TIMEOUT(watch_mix, MIX_WAIT_TIME, md);
2386 }
2387 }
2388 x = move_axis(cp, x); /* calls update_graph eventually (in snd-chn.c reset_x_display) */
2389 axis_changed = true;
2390 }
2391 else
2392 {
2393 if (watch_mix_proc != 0)
2394 {
2395 stop_watch_mix_proc();
2396 watch_mix_proc = 0;
2397 }
2398 }
2399
2400 reflect_mix_change(mix_id);
2401 {
2402 move_mix_data *mmd;
2403 mmd = (move_mix_data *)malloc(sizeof(move_mix_data));
2404 mmd->beg = pos - orig_beg;
2405 mmd->axis_changed = axis_changed;
2406 for_each_syncd_mix(mix_id, move_syncd_mix, (void *)mmd); /* syncd mixes drag together */
2407 free(mmd);
2408 }
2409
2410 if ((axis_changed) ||
2411 (cp->sound->channel_style == CHANNELS_SUPERIMPOSED))
2412 display_channel_time_data(cp);
2413 else
2414 {
2415 mus_long_t cur_end;
2416 cur_end = ms->beg + ms->len;
2417 if (cur_end > drag_end)
2418 drag_end = cur_end;
2419 if (ms->beg < drag_beg)
2420 drag_beg = ms->beg;
2421 #if USE_MOTIF
2422 make_partial_graph(cp, drag_beg, drag_end);
2423 display_channel_mixes_with_bounds(cp, drag_beg, drag_end);
2424 #endif
2425 }
2426
2427 if (Xen_hook_has_list(mix_drag_hook))
2428 run_hook(mix_drag_hook,
2429 Xen_list_3(new_xen_mix(mix_id),
2430 C_int_to_Xen_integer(x),
2431 C_int_to_Xen_integer(y)),
2432 S_mix_drag_hook);
2433 }
2434
2435
syncd_mix_set_position_1(mix_info * md,void * data)2436 static void syncd_mix_set_position_1(mix_info *md, void *data)
2437 {
2438 move_mix_data *mmd = (move_mix_data *)data;
2439 mix_set_position_edit(md->id, syncd_mix_position(md->id) + mmd->beg);
2440 after_edit(md->cp);
2441 update_graph(md->cp);
2442 }
2443
2444
syncd_mix_set_position(int mix_id,mus_long_t pos)2445 static void syncd_mix_set_position(int mix_id, mus_long_t pos)
2446 {
2447 move_mix_data *pos_data;
2448 pos_data = (move_mix_data *)malloc(sizeof(move_mix_data));
2449 pos_data->beg = pos;
2450 for_each_syncd_mix(mix_id, syncd_mix_set_position_1, pos_data);
2451 free(pos_data);
2452 }
2453
2454
finish_moving_mix_tag(int mix_id,int x)2455 void finish_moving_mix_tag(int mix_id, int x)
2456 {
2457 /* from mouse release after tag drag in snd-chn.c only */
2458 mix_info *md;
2459 mus_long_t pos;
2460 Xen res = Xen_false;
2461 chan_info *cp;
2462
2463 mix_dragged = false;
2464 if (watch_mix_proc != 0)
2465 {
2466 stop_watch_mix_proc();
2467 watch_mix_proc = 0;
2468 }
2469
2470 md = md_from_id(mix_id);
2471 if (!md) return;
2472 cp = md->cp;
2473
2474 pos = snd_round_mus_long_t(ungrf_x(cp->axis, x) * (double)(snd_srate(cp->sound)));
2475 if (pos < 0) pos = 0;
2476 cp->hookable = hookable_before_drag;
2477
2478 if (cp->edit_ctr > edpos_before_drag) /* possibly dragged it back to start point, so no edit took place */
2479 cp->edit_ctr--;
2480 keep_dragging_syncd_mixes(mix_id); /* fixup edpos */
2481
2482 if (Xen_hook_has_list(mix_release_hook))
2483 res = run_progn_hook(mix_release_hook,
2484 Xen_list_2(new_xen_mix(mix_id),
2485 C_llong_to_Xen_llong(pos - mix_position_from_id(mix_id))),
2486 S_mix_release_hook);
2487
2488 if (!(Xen_is_true(res)))
2489 {
2490 mus_long_t old_pos;
2491 old_pos = mix_position_from_id(mix_id);
2492 if (mix_set_position_edit(mix_id, pos))
2493 {
2494 cursor_sample(cp) = pos;
2495 after_edit(cp);
2496 update_graph(cp); /* this causes flashing, but it's next to impossible to fix
2497 * display_channel_id assumes previous id was erased, as does any after_graph_hook function
2498 * and we have to run lisp/fft graphs in any case (and the hook),
2499 * but display_channel_data_1 erases the old graph, so it's hard to specialize for this case
2500 */
2501 syncd_mix_set_position(mix_id, pos - old_pos); /* assumes syncd_mixes list exists */
2502 }
2503 }
2504 stop_dragging_syncd_mixes(mix_id);
2505 }
2506
2507
2508 /* View:Mixes dialog display */
2509
mix_display_during_drag(int mix_id,mus_long_t drag_beg,mus_long_t drag_end)2510 void mix_display_during_drag(int mix_id, mus_long_t drag_beg, mus_long_t drag_end)
2511 {
2512 chan_info *cp;
2513 cp = mix_chan_info_from_id(mix_id);
2514
2515 if (cp->sound->channel_style == CHANNELS_SUPERIMPOSED)
2516 display_channel_time_data(cp);
2517 else
2518 {
2519 #if USE_MOTIF
2520 mus_long_t cur_end, ms_beg;
2521 ms_beg = mix_position_from_id(mix_id);
2522 cur_end = ms_beg + mix_length_from_id(mix_id);
2523 if (cur_end > drag_end)
2524 drag_end = cur_end;
2525 if (ms_beg < drag_beg)
2526 drag_beg = ms_beg;
2527 make_partial_graph(cp, drag_beg, drag_end);
2528 display_channel_mixes_with_bounds(cp, drag_beg, drag_end);
2529 #endif
2530 }
2531 }
2532
2533
2534
snd_no_such_mix_error(const char * caller,Xen n)2535 static Xen snd_no_such_mix_error(const char *caller, Xen n)
2536 {
2537 Xen_error(Xen_make_error_type("no-such-mix"),
2538 Xen_list_3(C_string_to_Xen_string("~A: no such mix, ~A"),
2539 C_string_to_Xen_string(caller),
2540 n));
2541 return(Xen_false);
2542 }
2543
2544
after_mix_edit(int id)2545 void after_mix_edit(int id)
2546 {
2547 mix_info *md;
2548 md = md_from_id(id);
2549 if ((md) && (md->cp))
2550 {
2551 after_edit(md->cp);
2552 update_graph(md->cp);
2553 }
2554 }
2555
2556
2557
2558
2559 /* ---------------------------------------- syncd mixes ---------------------------------------- */
2560
2561 typedef struct {
2562 int mix_id, orig_edpos;
2563 mus_long_t orig_beg;
2564 chan_info *cp;
2565 } syncd_mix_info;
2566
2567 static syncd_mix_info *syncd_mixes = NULL;
2568 static int syncd_mixes_length = 0;
2569
2570
syncd_mix_position(int id)2571 static mus_long_t syncd_mix_position(int id)
2572 {
2573 if (syncd_mixes)
2574 {
2575 int i;
2576 for (i = 0; i < syncd_mixes_length; i++)
2577 if (id == syncd_mixes[i].mix_id)
2578 return(syncd_mixes[i].orig_beg);
2579 }
2580 return(-1);
2581 }
2582
2583
add_syncd_mix(mix_info * md,void * ignore)2584 static void add_syncd_mix(mix_info *md, void *ignore)
2585 {
2586 mus_long_t pos;
2587 int i, mix_id;
2588
2589 mix_id = md->id;
2590 pos = mix_position_from_id(mix_id);
2591
2592 i = syncd_mixes_length++;
2593 syncd_mixes[i].mix_id = mix_id;
2594 syncd_mixes[i].orig_beg = pos;
2595 syncd_mixes[i].orig_edpos = md->cp->edit_ctr;
2596 syncd_mixes[i].cp = md->cp;
2597 }
2598
2599
count_syncd_mixes(mix_info * md,void * ignore)2600 static void count_syncd_mixes(mix_info *md, void *ignore)
2601 {
2602 syncd_mixes_length++;
2603 }
2604
2605
start_dragging_syncd_mixes(int mix_id)2606 void start_dragging_syncd_mixes(int mix_id)
2607 {
2608 syncd_mixes_length = 0;
2609 for_each_syncd_mix(mix_id, count_syncd_mixes, NULL);
2610 if (syncd_mixes_length > 0)
2611 {
2612 if (syncd_mixes) free(syncd_mixes);
2613 syncd_mixes = (syncd_mix_info *)calloc(syncd_mixes_length, sizeof(syncd_mix_info));
2614 syncd_mixes_length = 0;
2615 for_each_syncd_mix(mix_id, add_syncd_mix, NULL);
2616 }
2617 }
2618
keep_dragging_syncd_mixes(int mix_id)2619 void keep_dragging_syncd_mixes(int mix_id)
2620 {
2621 int i;
2622 for (i = 0; i < syncd_mixes_length; i++)
2623 syncd_mixes[i].cp->edit_ctr = syncd_mixes[i].orig_edpos;
2624 }
2625
stop_dragging_syncd_mixes(int mix_id)2626 void stop_dragging_syncd_mixes(int mix_id)
2627 {
2628 /* undo edit? */
2629 if (syncd_mixes)
2630 {
2631 free(syncd_mixes);
2632 syncd_mixes = NULL;
2633 syncd_mixes_length = 0;
2634 }
2635 }
2636
2637
syncd_mix_change_position_1(mix_info * md,void * data)2638 static void syncd_mix_change_position_1(mix_info *md, void *data)
2639 {
2640 move_mix_data *mmd = (move_mix_data *)data;
2641 mix_set_position_edit(md->id, mix_position_from_id(md->id) + mmd->beg);
2642 if (!mmd->axis_changed)
2643 mix_display_during_drag(md->id, drag_beg, drag_end);
2644 }
2645
2646
syncd_mix_change_position(int mix_id,mus_long_t change)2647 void syncd_mix_change_position(int mix_id, mus_long_t change)
2648 {
2649 move_mix_data *pos_data;
2650 pos_data = (move_mix_data *)malloc(sizeof(move_mix_data));
2651 pos_data->beg = change;
2652 for_each_syncd_mix(mix_id, syncd_mix_change_position_1, pos_data);
2653 free(pos_data);
2654 }
2655
2656
2657 static int ms_chans = 0;
2658 static chan_info **ms_cps = NULL;
2659
update_syncd_chans(mix_info * md,void * ignore)2660 static void update_syncd_chans(mix_info *md, void *ignore)
2661 {
2662 int i;
2663 for (i = 0; i < ms_chans; i++)
2664 if (md->cp == ms_cps[i])
2665 return;
2666 ms_cps[ms_chans++] = md->cp;
2667 update_graph(md->cp);
2668 }
2669
2670
after_syncd_mix_edit(int id)2671 void after_syncd_mix_edit(int id)
2672 {
2673 ms_chans = active_channels(WITH_VIRTUAL_CHANNELS);
2674 if (ms_chans > 1)
2675 {
2676 ms_cps = (chan_info **)calloc(ms_chans, sizeof(chan_info *));
2677 ms_chans = 1;
2678 ms_cps[0] = mix_chan_info_from_id(id); /* base cp handled elsewhere */
2679 for_each_syncd_mix(id, update_syncd_chans, NULL);
2680 ms_chans = 0;
2681 free(ms_cps);
2682 ms_cps = NULL;
2683 }
2684 }
2685
2686
2687
2688
2689
2690 /* ---------------------------------------- mix objects ---------------------------------------- */
2691
2692 typedef struct {
2693 int n;
2694 } xen_mix;
2695
2696 /* md->cp->sounds[md->original_index] is the original mix data */
2697
2698
2699 #define Xen_to_xen_mix(arg) ((xen_mix *)Xen_object_ref(arg))
2700
xen_mix_to_int(Xen n)2701 int xen_mix_to_int(Xen n)
2702 {
2703 xen_mix *mx;
2704 mx = Xen_to_xen_mix(n);
2705 return(mx->n);
2706 }
2707
2708
2709 static Xen_object_type_t xen_mix_tag;
2710
xen_is_mix(Xen obj)2711 bool xen_is_mix(Xen obj)
2712 {
2713 return(Xen_c_object_is_type(obj, xen_mix_tag));
2714 }
2715
2716 #if (!HAVE_SCHEME)
xen_mix_free(xen_mix * v)2717 static void xen_mix_free(xen_mix *v) {if (v) free(v);}
2718
Xen_wrap_free(xen_mix,free_xen_mix,xen_mix_free)2719 Xen_wrap_free(xen_mix, free_xen_mix, xen_mix_free)
2720 #else
2721 static s7_pointer s7_xen_mix_free(s7_scheme *sc, s7_pointer obj)
2722 {
2723 xen_mix *v;
2724 v = (xen_mix *)s7_c_object_value(obj);
2725 if (v) free(v);
2726 return(NULL);
2727 }
2728 #endif
2729
2730
2731 static char *xen_mix_to_string(xen_mix *v)
2732 {
2733 #define xen_is_mixRINT_BUFFER_SIZE 64
2734 char *buf;
2735 if (!v) return(NULL);
2736 buf = (char *)calloc(xen_is_mixRINT_BUFFER_SIZE, sizeof(char));
2737 snprintf(buf, xen_is_mixRINT_BUFFER_SIZE, "#<mix %d>", v->n);
2738 return(buf);
2739 }
2740
2741
2742 #if HAVE_FORTH || HAVE_RUBY
Xen_wrap_print(xen_mix,print_xen_mix,xen_mix_to_string)2743 Xen_wrap_print(xen_mix, print_xen_mix, xen_mix_to_string)
2744
2745 static Xen g_xen_mix_to_string(Xen obj)
2746 {
2747 char *vstr;
2748 Xen result;
2749 #define S_xen_mix_to_string "mix->string"
2750 Xen_check_type(xen_is_mix(obj), obj, 1, S_xen_mix_to_string, "a mix");
2751 vstr = xen_mix_to_string(Xen_to_xen_mix(obj));
2752 result = C_string_to_Xen_string(vstr);
2753 free(vstr);
2754 return(result);
2755 }
2756 #else
2757 #if HAVE_SCHEME
g_xen_mix_to_string(s7_scheme * sc,s7_pointer args)2758 static s7_pointer g_xen_mix_to_string(s7_scheme *sc, s7_pointer args)
2759 {
2760 char *vstr;
2761 Xen result;
2762 vstr = xen_mix_to_string(Xen_to_xen_mix(s7_car(args)));
2763 result = C_string_to_Xen_string(vstr);
2764 free(vstr);
2765 return(result);
2766 }
2767 #endif
2768 #endif
2769
2770 #if HAVE_SCHEME
2771 static s7_pointer g_mix_methods = NULL;
2772 static s7_pointer g_mix_sampler_methods = NULL;
2773 static s7_pointer mix_to_let_func = NULL;
2774 static s7_pointer mix_sampler_to_let_func = NULL;
2775 #else
2776 /* !HAVE_SCHEME */
xen_mix_equalp(xen_mix * v1,xen_mix * v2)2777 static bool xen_mix_equalp(xen_mix *v1, xen_mix *v2)
2778 {
2779 return((v1 == v2) ||
2780 (v1->n == v2->n));
2781 }
2782
equalp_xen_mix(Xen obj1,Xen obj2)2783 static Xen equalp_xen_mix(Xen obj1, Xen obj2)
2784 {
2785 if ((!(xen_is_mix(obj1))) || (!(xen_is_mix(obj2)))) return(Xen_false);
2786 return(C_bool_to_Xen_boolean(xen_mix_equalp(Xen_to_xen_mix(obj1), Xen_to_xen_mix(obj2))));
2787 }
2788 #endif
2789
2790
xen_mix_make(int n)2791 static xen_mix *xen_mix_make(int n)
2792 {
2793 xen_mix *new_v;
2794 new_v = (xen_mix *)malloc(sizeof(xen_mix));
2795 new_v->n = n;
2796 return(new_v);
2797 }
2798
2799
new_xen_mix(int n)2800 Xen new_xen_mix(int n)
2801 {
2802 xen_mix *mx;
2803 if (n < 0)
2804 return(Xen_false);
2805
2806 mx = xen_mix_make(n);
2807 #if HAVE_SCHEME
2808 {
2809 s7_pointer m;
2810 m = Xen_make_object(xen_mix_tag, mx, 0, free_xen_mix);
2811 s7_c_object_set_let(s7, m, g_mix_methods);
2812 return(m);
2813 }
2814 #else
2815 return(Xen_make_object(xen_mix_tag, mx, 0, free_xen_mix));
2816 #endif
2817 }
2818
2819
2820 #if HAVE_SCHEME
s7_xen_mix_length(s7_scheme * sc,s7_pointer args)2821 static Xen s7_xen_mix_length(s7_scheme *sc, s7_pointer args)
2822 {
2823 return(g_mix_length(s7_car(args)));
2824 }
2825
2826
s7_xen_mix_is_equal(s7_scheme * sc,s7_pointer args)2827 static s7_pointer s7_xen_mix_is_equal(s7_scheme *sc, s7_pointer args)
2828 {
2829 s7_pointer p1, p2;
2830 p1 = s7_car(args);
2831 p2 = s7_cadr(args);
2832 if (p1 == p2) return(s7_t(sc));
2833 if (s7_c_object_type(p2) == xen_mix_tag)
2834 return(s7_make_boolean(sc, (((xen_mix *)s7_c_object_value(p1))->n == ((xen_mix *)s7_c_object_value(p2))->n)));
2835 return(s7_f(sc));
2836 }
2837
2838
s7_xen_mix_copy(s7_scheme * sc,s7_pointer args)2839 static Xen s7_xen_mix_copy(s7_scheme *sc, s7_pointer args)
2840 {
2841 s7_pointer obj;
2842 obj = s7_car(args);
2843 return(new_xen_mix(copy_mix(Xen_mix_to_C_int(obj))));
2844 }
2845 #endif
2846
2847
init_xen_mix(void)2848 static void init_xen_mix(void)
2849 {
2850 #if HAVE_SCHEME
2851 g_mix_methods = s7_openlet(s7, s7_inlet(s7, s7_list(s7, 2, s7_make_symbol(s7, "object->let"), mix_to_let_func)));
2852 s7_gc_protect(s7, g_mix_methods);
2853 xen_mix_tag = s7_make_c_type(s7, "<mix>");
2854 s7_c_type_set_gc_free(s7, xen_mix_tag, s7_xen_mix_free);
2855 s7_c_type_set_is_equal(s7, xen_mix_tag, s7_xen_mix_is_equal);
2856 s7_c_type_set_length(s7, xen_mix_tag, s7_xen_mix_length);
2857 s7_c_type_set_copy(s7, xen_mix_tag, s7_xen_mix_copy);
2858 s7_c_type_set_to_string(s7, xen_mix_tag, g_xen_mix_to_string);
2859 #else
2860 #if HAVE_RUBY
2861 xen_mix_tag = Xen_make_object_type("XenMix", sizeof(xen_mix));
2862 #else
2863 xen_mix_tag = Xen_make_object_type("Mix", sizeof(xen_mix));
2864 #endif
2865 #endif
2866
2867 #if HAVE_FORTH
2868 fth_set_object_inspect(xen_mix_tag, print_xen_mix);
2869 fth_set_object_dump(xen_mix_tag, g_xen_mix_to_string);
2870 fth_set_object_equal(xen_mix_tag, equalp_xen_mix);
2871 fth_set_object_length(xen_mix_tag, g_mix_length);
2872 fth_set_object_free(xen_mix_tag, free_xen_mix);
2873 #endif
2874
2875 #if HAVE_RUBY
2876 rb_define_method(xen_mix_tag, "to_s", Xen_procedure_cast print_xen_mix, 0);
2877 rb_define_method(xen_mix_tag, "eql?", Xen_procedure_cast equalp_xen_mix, 1);
2878 rb_define_method(xen_mix_tag, "==", Xen_procedure_cast equalp_xen_mix, 1);
2879 rb_define_method(xen_mix_tag, "length", Xen_procedure_cast g_mix_length, 0);
2880 rb_define_method(xen_mix_tag, "to_str", Xen_procedure_cast g_xen_mix_to_string, 0);
2881 #endif
2882 }
2883 /* -------------------------------------------------------------------------------- */
2884
2885
g_integer_to_mix(Xen n)2886 static Xen g_integer_to_mix(Xen n)
2887 {
2888 #define H_integer_to_mix "(" S_integer_to_mix " n) returns a mix object corresponding to the given integer"
2889 Xen_check_type(Xen_is_integer(n), n, 1, S_integer_to_mix, "an integer");
2890 if (mix_is_active(Xen_integer_to_C_int(n)))
2891 return(new_xen_mix(Xen_integer_to_C_int(n)));
2892 return(Xen_false);
2893 }
2894
2895
g_mix_to_integer(Xen n)2896 static Xen g_mix_to_integer(Xen n)
2897 {
2898 #define H_mix_to_integer "(" S_mix_to_integer " id) returns the integer corresponding to the given mix object"
2899 Xen_check_type(xen_is_mix(n), n, 1, S_mix_to_integer, "a mix");
2900 return(C_int_to_Xen_integer(xen_mix_to_int(n)));
2901 }
2902
2903
g_mix_length(Xen n)2904 Xen g_mix_length(Xen n)
2905 {
2906 #define H_mix_length "(" S_mix_length " id): mix's length in samples"
2907 int id;
2908 Xen_check_type(xen_is_mix(n), n, 1, S_mix_length, "a mix");
2909 id = Xen_mix_to_C_int(n);
2910 if (mix_is_active(id))
2911 return(C_llong_to_Xen_llong(mix_length_from_id(id)));
2912 return(snd_no_such_mix_error(S_mix_length, n));
2913 }
2914
2915
g_mix_position(Xen n)2916 static Xen g_mix_position(Xen n)
2917 {
2918 int id;
2919 #define H_mix_position "(" S_mix_position " id): mix's begin time in the output in samples"
2920 Xen_check_type(xen_is_mix(n), n, 1, S_mix_position, "a mix");
2921 id = Xen_mix_to_C_int(n);
2922 if (mix_is_active(id))
2923 return(C_llong_to_Xen_llong(mix_position_from_id(id)));
2924 return(snd_no_such_mix_error(S_mix_position, n));
2925 }
2926
2927
g_set_mix_position(Xen n,Xen pos)2928 static Xen g_set_mix_position(Xen n, Xen pos)
2929 {
2930 int id;
2931 mus_long_t beg;
2932 Xen_check_type(xen_is_mix(n), n, 1, S_set S_mix_position, "a mix");
2933 Xen_check_type(Xen_is_llong(pos), pos, 2, S_set S_mix_position, "an integer");
2934
2935 id = Xen_mix_to_C_int(n);
2936 if (!(mix_is_active(id)))
2937 return(snd_no_such_mix_error(S_set S_mix_position, n));
2938
2939 beg = beg_to_sample(pos, S_set S_mix_position);
2940 if (mix_set_position_edit(id, beg))
2941 after_mix_edit(id);
2942 return(pos);
2943 }
2944
2945
g_mix_amp(Xen n)2946 static Xen g_mix_amp(Xen n)
2947 {
2948 #define H_mix_amp "(" S_mix_amp " id): mix's scaler"
2949 int id;
2950
2951 Xen_check_type(xen_is_mix(n), n, 1, S_mix_amp, "a mix");
2952 id = Xen_mix_to_C_int(n);
2953
2954 if (mix_is_active(id))
2955 return(C_double_to_Xen_real(mix_amp_from_id(id)));
2956 return(snd_no_such_mix_error(S_mix_amp, n));
2957 }
2958
2959
g_set_mix_amp(Xen n,Xen uval)2960 static Xen g_set_mix_amp(Xen n, Xen uval)
2961 {
2962 int id;
2963 Xen_check_type(xen_is_mix(n), n, 1, S_set S_mix_amp, "a mix");
2964 Xen_check_type(Xen_is_number(uval), uval, 2, S_set S_mix_amp, "a number");
2965
2966 id = Xen_mix_to_C_int(n);
2967 if (!(mix_is_active(id)))
2968 return(snd_no_such_mix_error(S_set S_mix_amp, n));
2969
2970 if (mix_set_amp_edit(id, Xen_real_to_C_double(uval)))
2971 after_mix_edit(id);
2972
2973 return(uval);
2974 }
2975
2976
g_mix_amp_env(Xen n)2977 static Xen g_mix_amp_env(Xen n)
2978 {
2979 #define H_mix_amp_env "(" S_mix_amp_env " id): amplitude envelope applied to mix"
2980 int id;
2981
2982 Xen_check_type(xen_is_mix(n), n, 1, S_mix_amp_env, "a mix");
2983 id = Xen_mix_to_C_int(n);
2984
2985 if (mix_is_active(id))
2986 return(env_to_xen(mix_amp_env_from_id(id)));
2987 return(snd_no_such_mix_error(S_mix_amp_env, n));
2988 }
2989
2990
g_set_mix_amp_env(Xen n,Xen val)2991 static Xen g_set_mix_amp_env(Xen n, Xen val)
2992 {
2993 env *e = NULL;
2994 int id;
2995 Xen_check_type(xen_is_mix(n), n, 1, S_set S_mix_amp_env, "a mix");
2996 Xen_check_type(Xen_is_list(val) || Xen_is_false(val), val, 2, S_set S_mix_amp_env, "a list or " PROC_FALSE);
2997
2998 id = Xen_mix_to_C_int(n);
2999 if (!(mix_is_active(id)))
3000 return(snd_no_such_mix_error(S_set S_mix_amp_env, n));
3001
3002 if (Xen_is_list(val))
3003 e = get_env(val, S_set S_mix_amp_env);
3004
3005 if (mix_set_amp_env_edit(id, e))
3006 after_mix_edit(id);
3007
3008 /* e is copied by mix_set_amp_env_edit, and created by get_env (xen_to_env), so it should be freed here */
3009 if (e) free_env(e);
3010 return(val);
3011 }
3012
3013
g_mix_speed(Xen n)3014 static Xen g_mix_speed(Xen n)
3015 {
3016 #define H_mix_speed "(" S_mix_speed " id): mix's resampling ratio"
3017 int id;
3018 Xen_check_type(xen_is_mix(n), n, 1, S_mix_speed, "a mix");
3019 id = Xen_mix_to_C_int(n);
3020 if (mix_is_active(id))
3021 return(C_double_to_Xen_real(mix_speed_from_id(id)));
3022 return(snd_no_such_mix_error(S_mix_speed, n));
3023 }
3024
3025
g_set_mix_speed(Xen n,Xen uval)3026 static Xen g_set_mix_speed(Xen n, Xen uval)
3027 {
3028 int id;
3029 Xen_check_type(xen_is_mix(n), n, 1, S_set S_mix_speed, "a mix");
3030 Xen_check_type(Xen_is_number(uval), uval, 2, S_set S_mix_speed, "a number");
3031
3032 id = Xen_mix_to_C_int(n);
3033 if (!(mix_is_active(id)))
3034 return(snd_no_such_mix_error(S_set S_mix_speed, n));
3035
3036 if (mix_set_speed_edit(id, Xen_real_to_C_double(uval)))
3037 after_mix_edit(id);
3038
3039 return(uval);
3040 }
3041
3042
g_mix_name(Xen n)3043 static Xen g_mix_name(Xen n)
3044 {
3045 #define H_mix_name "(" S_mix_name " id): name of mix"
3046 int id;
3047 Xen_check_type(xen_is_mix(n), n, 1, S_mix_name, "a mix");
3048 id = Xen_mix_to_C_int(n);
3049 if (mix_exists(id))
3050 return(C_string_to_Xen_string(mix_name_from_id(id)));
3051 return(snd_no_such_mix_error(S_mix_name, n));
3052 }
3053
3054
g_set_mix_name(Xen n,Xen val)3055 static Xen g_set_mix_name(Xen n, Xen val)
3056 {
3057 int id;
3058 Xen_check_type(xen_is_mix(n), n, 1, S_set S_mix_name, "a mix");
3059 Xen_check_type(Xen_is_string(val) || Xen_is_false(val), val, 2, S_set S_mix_name, "a string");
3060 id = Xen_mix_to_C_int(n);
3061 if (mix_exists(id))
3062 {
3063 mix_set_name_from_id(id, (Xen_is_string(val) ? Xen_string_to_C_string(val) : NULL));
3064 update_graph(mix_chan_info_from_id(id));
3065 }
3066 else return(snd_no_such_mix_error(S_set S_mix_name, n));
3067 return(val);
3068 }
3069
3070
g_mix_sync(Xen n)3071 Xen g_mix_sync(Xen n)
3072 {
3073 #define H_mix_sync "(" S_mix_sync " id): mix sync field (an integer)"
3074 int id;
3075 Xen_check_type(xen_is_mix(n), n, 1, S_mix_sync, "a mix");
3076 id = Xen_mix_to_C_int(n);
3077 if (mix_exists(id))
3078 return(C_int_to_Xen_integer(mix_sync_from_id(id)));
3079 return(snd_no_such_mix_error(S_mix_sync, n));
3080 }
3081
3082
g_set_mix_sync(Xen n,Xen val)3083 Xen g_set_mix_sync(Xen n, Xen val)
3084 {
3085 int id;
3086 Xen_check_type(xen_is_mix(n), n, 1, S_set S_mix_sync, "a mix");
3087 Xen_check_type(Xen_is_integer(val), val, 2, S_set S_mix_sync, "an integer");
3088 id = Xen_mix_to_C_int(n);
3089 if (mix_exists(id))
3090 mix_set_sync_from_id(id, Xen_integer_to_C_int(val));
3091 else return(snd_no_such_mix_error(S_set S_mix_sync, n));
3092 return(val);
3093 }
3094
3095
g_mix_sync_max(void)3096 static Xen g_mix_sync_max(void)
3097 {
3098 #define H_mix_sync_max "(" S_mix_sync_max "): max mix sync value seen so far"
3099 return(C_int_to_Xen_integer(current_mix_sync_max));
3100 }
3101
3102
g_mix_tag_y(Xen n)3103 static Xen g_mix_tag_y(Xen n)
3104 {
3105 #define H_mix_tag_y "(" S_mix_tag_y " id): height of mix's tag"
3106 int id;
3107 Xen_check_type(xen_is_mix(n), n, 1, S_mix_tag_y, "a mix");
3108 id = Xen_mix_to_C_int(n);
3109 if (mix_exists(id))
3110 return(C_int_to_Xen_integer(mix_tag_y_from_id(id)));
3111 return(snd_no_such_mix_error(S_mix_tag_y, n));
3112 }
3113
3114
g_set_mix_tag_y(Xen n,Xen val)3115 static Xen g_set_mix_tag_y(Xen n, Xen val)
3116 {
3117 int id;
3118
3119 Xen_check_type(xen_is_mix(n), n, 1, S_set S_mix_tag_y, "a mix");
3120 Xen_check_type(Xen_is_integer(val), val, 2, S_set S_mix_tag_y, "an integer");
3121 id = Xen_mix_to_C_int(n);
3122
3123 if (mix_exists(id))
3124 {
3125 chan_info *cp;
3126 mix_state *ms;
3127 mix_info *md;
3128
3129 md = md_from_id(id);
3130 ms = current_mix_state(md);
3131 cp = md->cp;
3132 if ((cp) &&
3133 (!(cp->squelch_update)))
3134 {
3135 graphics_context *ax;
3136 ax = erase_context(cp);
3137 erase_mix_tag_and_waveform(ms, cp, cp->axis, ax, md->x, cp->axis->y_offset + md->tag_y + MIX_TAG_Y_OFFSET);
3138 md->tag_y = Xen_integer_to_C_int(val);
3139 display_one_mix(ms, cp);
3140 }
3141 else md->tag_y = Xen_integer_to_C_int(val);
3142 }
3143 else return(snd_no_such_mix_error(S_set S_mix_tag_y, n));
3144
3145 return(val);
3146 }
3147
3148
g_mix_properties(Xen n)3149 static Xen g_mix_properties(Xen n)
3150 {
3151 #define H_mix_properties "(" S_mix_properties " id): A property list associated with the given mix."
3152
3153 mix_info *md;
3154 Xen_check_type(xen_is_mix(n), n, 1, S_mix_properties, "a mix");
3155
3156 md = md_from_id(Xen_mix_to_C_int(n));
3157 if (!md)
3158 return(snd_no_such_mix_error(S_mix_properties, n));
3159
3160 if (!(Xen_is_vector(md->properties)))
3161 {
3162 md->properties = Xen_make_vector(1, Xen_empty_list);
3163 md->properties_gc_loc = snd_protect(md->properties);
3164 }
3165 return(Xen_vector_ref(md->properties, 0));
3166 }
3167
3168
g_set_mix_properties(Xen n,Xen val)3169 static Xen g_set_mix_properties(Xen n, Xen val)
3170 {
3171 mix_info *md;
3172 Xen_check_type(xen_is_mix(n), n, 1, S_mix_properties, "a mix");
3173
3174 md = md_from_id(Xen_mix_to_C_int(n));
3175 if (!md)
3176 return(snd_no_such_mix_error(S_set S_mix_properties, n));
3177
3178 if (!(Xen_is_vector(md->properties)))
3179 {
3180 md->properties = Xen_make_vector(1, Xen_empty_list);
3181 md->properties_gc_loc = snd_protect(md->properties);
3182 }
3183 Xen_vector_set(md->properties, 0, val);
3184 return(Xen_vector_ref(md->properties, 0));
3185 }
3186
3187
g_mix_property(Xen key,Xen id)3188 static Xen g_mix_property(Xen key, Xen id)
3189 {
3190 #define H_mix_property "(" S_mix_property " key id) returns the value associated with 'key' in the given mix's property list, or " PROC_FALSE "."
3191 Xen_check_type(xen_is_mix(id), id, 2, S_mix_property, "a mix");
3192 return(Xen_assoc_ref(key, g_mix_properties(id)));
3193 }
3194
g_set_mix_property(Xen key,Xen id,Xen val)3195 static Xen g_set_mix_property(Xen key, Xen id, Xen val)
3196 {
3197 Xen_check_type(xen_is_mix(id), id, 2, S_mix_property, "a mix");
3198 g_set_mix_properties(id, Xen_assoc_set(key, val, g_mix_properties(id)));
3199 return(val);
3200 }
3201
3202
g_mix_home(Xen n)3203 static Xen g_mix_home(Xen n)
3204 {
3205 #define H_mix_home "(" S_mix_home " id): list of sound index and channel for the output of the mix, and the \
3206 filename or " PROC_FALSE " and the input channel for its data."
3207 mix_info *md;
3208
3209 Xen_check_type(xen_is_mix(n), n, 1, S_mix_home, "a mix");
3210 md = md_from_id(Xen_mix_to_C_int(n));
3211 if (!md)
3212 return(snd_no_such_mix_error(S_mix_home, n));
3213
3214 return(Xen_list_4(C_int_to_Xen_sound((md->cp->sound)->index),
3215 C_int_to_Xen_integer((md->cp->chan)),
3216 (md->in_filename) ? C_string_to_Xen_string(md->in_filename) : Xen_false,
3217 C_int_to_Xen_integer(md->in_chan)));
3218 }
3219
3220
color_mixes(color_t color)3221 void color_mixes(color_t color)
3222 {
3223 int i;
3224 set_mix_color(color);
3225 for (i = 0; i < mix_infos_ctr; i++)
3226 if (mix_infos[i])
3227 {
3228 mix_infos[i]->color = color;
3229 mix_infos[i]->original_color = color;
3230 }
3231 }
3232
3233
mix_maxamp(int mix_id)3234 static double mix_maxamp(int mix_id)
3235 {
3236 mix_info *md;
3237 mix_state *ms;
3238 snd_fd *sf;
3239 mus_long_t n;
3240 mus_float_t mx = 0.0;
3241
3242 md = md_from_id(mix_id);
3243 if (!md)
3244 return(0.0);
3245
3246 ms = current_mix_state(md);
3247 sf = make_virtual_mix_reader(md->cp, 0, ms->len, ms->index, 1.0, READ_FORWARD);
3248 for (n = 0; n < ms->len; n++)
3249 {
3250 mus_float_t val;
3251 val = fabs(read_sample(sf));
3252 if (val > mx) mx = val;
3253 }
3254 free_snd_fd(sf);
3255 return(mx);
3256 }
3257
3258
g_mix_maxamp(Xen mix_id)3259 Xen g_mix_maxamp(Xen mix_id)
3260 {
3261 return(C_double_to_Xen_real(mix_maxamp(Xen_mix_to_C_int(mix_id))));
3262 }
3263
3264
3265 /* mix-related globals */
3266
update_mix_waveforms(chan_info * cp)3267 static void update_mix_waveforms(chan_info *cp)
3268 {
3269 if ((cp) && (channel_has_active_mixes(cp)))
3270 update_graph(cp);
3271 }
3272
3273
set_mix_waveform_height(int new_val)3274 void set_mix_waveform_height(int new_val)
3275 {
3276 in_set_mix_waveform_height(new_val);
3277 for_each_normal_chan(update_mix_waveforms);
3278 }
3279
3280
g_mix_waveform_height(void)3281 static Xen g_mix_waveform_height(void)
3282 {
3283 #define H_mix_waveform_height "(" S_mix_waveform_height "): max height (pixels) of mix waveforms (20)"
3284 return(C_int_to_Xen_integer(mix_waveform_height(ss)));
3285 }
3286
3287
g_set_mix_waveform_height(Xen val)3288 static Xen g_set_mix_waveform_height(Xen val)
3289 {
3290 int new_val;
3291 Xen_check_type(Xen_is_integer(val), val, 1, S_set S_mix_waveform_height, "an integer");
3292 new_val = mus_iclamp(0, Xen_integer_to_C_int(val), LOTSA_PIXELS);
3293 set_mix_waveform_height(new_val);
3294 return(C_int_to_Xen_integer(mix_waveform_height(ss)));
3295 }
3296
3297
g_with_mix_tags(void)3298 static Xen g_with_mix_tags(void)
3299 {
3300 #define H_with_mix_tags "(" S_with_mix_tags "): " PROC_TRUE " if Snd should try to use virtual (tagged) mixing"
3301 return(C_bool_to_Xen_boolean(with_mix_tags(ss)));
3302 }
3303
3304
g_set_with_mix_tags(Xen val)3305 static Xen g_set_with_mix_tags(Xen val)
3306 {
3307 Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_with_mix_tags, "a boolean");
3308 set_with_mix_tags(Xen_boolean_to_C_bool(val));
3309 return(C_bool_to_Xen_boolean(with_mix_tags(ss)));
3310 }
3311
3312
g_mix_tag_width(void)3313 static Xen g_mix_tag_width(void)
3314 {
3315 #define H_mix_tag_width "(" S_mix_tag_width "): width (pixels) of mix tags (6)"
3316 return(C_int_to_Xen_integer(mix_tag_width(ss)));
3317 }
3318
3319
g_set_mix_tag_width(Xen val)3320 static Xen g_set_mix_tag_width(Xen val)
3321 {
3322 int width;
3323 Xen_check_type(Xen_is_integer(val), val, 1, S_set S_mix_tag_width, "an integer");
3324 width = mus_iclamp(0, Xen_integer_to_C_int(val), LOTSA_PIXELS);
3325 set_mix_tag_width(width);
3326 for_each_normal_chan(update_graph);
3327 return(C_int_to_Xen_integer(mix_tag_width(ss)));
3328 }
3329
3330
g_mix_tag_height(void)3331 static Xen g_mix_tag_height(void)
3332 {
3333 #define H_mix_tag_height "(" S_mix_tag_height "): height (pixels) of mix tags (14)"
3334 return(C_int_to_Xen_integer(mix_tag_height(ss)));
3335 }
3336
3337
g_set_mix_tag_height(Xen val)3338 static Xen g_set_mix_tag_height(Xen val)
3339 {
3340 int height;
3341 Xen_check_type(Xen_is_integer(val), val, 1, S_set S_mix_tag_height, "an integer");
3342 height = mus_iclamp(0, Xen_integer_to_C_int(val), LOTSA_PIXELS);
3343 set_mix_tag_height(height);
3344 for_each_normal_chan(update_graph);
3345 return(C_int_to_Xen_integer(mix_tag_height(ss)));
3346 }
3347
3348
g_is_mix(Xen n)3349 static Xen g_is_mix(Xen n)
3350 {
3351 #define H_is_mix "(" S_is_mix " id): returns " PROC_TRUE " if the 'n' is a mix that exists somewhere in the edit list."
3352 return(C_bool_to_Xen_boolean((xen_is_mix(n)) && (mix_exists(Xen_mix_to_C_int(n)))));
3353 }
3354
3355
g_mixes(Xen snd,Xen chn)3356 static Xen g_mixes(Xen snd, Xen chn)
3357 {
3358 #define H_mixes "(" S_mixes " :optional snd chn): list of active mixes (ids) associated with snd and chn"
3359 snd_info *sp;
3360 Xen res1 = Xen_empty_list;
3361
3362 Snd_assert_channel(S_mixes, snd, chn, 0);
3363
3364 if (Xen_is_integer(snd) || xen_is_sound(snd))
3365 {
3366 int i;
3367 if (Xen_is_integer(chn))
3368 {
3369 /* scan all mixes for any associated with this channel */
3370 chan_info *cp;
3371 cp = get_cp(snd, chn, S_mixes);
3372 if (!cp) return(Xen_false);
3373 for (i = mix_infos_ctr - 1; i >= 0; i--)
3374 if ((mix_is_active(i)) &&
3375 (mix_infos[i]->cp == cp))
3376 res1 = Xen_cons(new_xen_mix(i), res1);
3377 }
3378 else
3379 {
3380 sp = get_sp(snd);
3381 if (!sp)
3382 return(snd_no_such_sound_error(S_mixes, snd));
3383 for (i = sp->nchans - 1; i >= 0; i--)
3384 res1 = Xen_cons(g_mixes(snd, C_int_to_Xen_integer(i)), res1);
3385 }
3386 }
3387 else
3388 {
3389 int j;
3390 for (j = ss->max_sounds - 1; j >= 0; j--)
3391 {
3392 sp = ss->sounds[j];
3393 if ((sp) && (sp->inuse == SOUND_NORMAL))
3394 res1 = Xen_cons(g_mixes(C_int_to_Xen_integer(j),
3395 Xen_undefined),
3396 res1);
3397 }
3398 }
3399 return(res1);
3400 }
3401
3402
3403
g_mix_vct(Xen obj,Xen beg,Xen snd,Xen chn,Xen with_tag,Xen origin)3404 static Xen g_mix_vct(Xen obj, Xen beg, Xen snd, Xen chn, Xen with_tag, Xen origin)
3405 {
3406 #define H_mix_vct "(" S_mix_vct " data :optional (beg 0) snd chn (with-tag " S_with_mix_tags ") origin): \
3407 mix data (a " S_vct ") into snd's channel chn starting at beg; return the new mix id, if any"
3408
3409 vct *v;
3410 mus_long_t bg;
3411 chan_info *cp;
3412 const char *edname = NULL;
3413 mus_float_t *data;
3414 int len, mix_id = NO_MIX_TAG;
3415 bool with_mixer;
3416
3417 Xen_check_type(mus_is_vct(obj), obj, 1, S_mix_vct, "a " S_vct);
3418 Snd_assert_channel(S_mix_vct, snd, chn, 3);
3419 Xen_check_type(Xen_is_integer_or_unbound(beg), beg, 2, S_mix_vct, "an integer");
3420 Xen_check_type(Xen_is_boolean_or_unbound(with_tag), with_tag, 5, S_mix_vct, "a boolean");
3421 Xen_check_type(Xen_is_string_or_unbound(origin), origin, 6, S_mix_vct, "a string");
3422
3423 cp = get_cp(snd, chn, S_mix_vct);
3424 if (!cp) return(Xen_false);
3425 if (!(is_editable(cp))) return(Xen_false);
3426
3427 if (Xen_is_string(origin))
3428 edname = Xen_string_to_C_string(origin);
3429 else edname = S_mix_vct;
3430
3431 bg = beg_to_sample(beg, S_mix_vct);
3432 v = Xen_to_vct(obj);
3433 len = mus_vct_length(v);
3434
3435 with_mixer = virtual_mix_ok(cp, cp->edit_ctr);
3436 if (with_mixer)
3437 {
3438 if (!Xen_is_bound(with_tag))
3439 with_mixer = with_mix_tags(ss);
3440 else with_mixer = Xen_boolean_to_C_bool(with_tag);
3441 }
3442
3443 if (!with_mixer)
3444 return(C_bool_to_Xen_boolean(mix_vct_untagged(v, cp, bg, edname)));
3445
3446 data = mus_vct_data(v);
3447
3448 /* make_snd_data_buffer copies the data array, so we don't need GC protection */
3449 /* we can't use v->data directly because the user might change it */
3450 {
3451 char *new_origin;
3452 /* (mix-vct (vct .1 .2 .3) 100 0 0 #t "mix-vct (vct .1 .2 .3)") */
3453
3454 #if HAVE_FORTH
3455 /* vct( 0.1 0.2 0.3 ) 100 snd chn #t "vct( 0.1 0.2, 0.3 )" mix-vct */
3456 {
3457 if (edname && *edname)
3458 {
3459 char *name;
3460 if ((name = strrchr(edname, ' ')))
3461 name++;
3462 else
3463 name = S_mix_vct;
3464 new_origin = mus_format("%.*s %" print_mus_long " snd chn %s to -mix-%d",
3465 (int)(strlen(edname) - strlen(name) - 1), edname,
3466 bg, name, mix_infos_ctr);
3467 }
3468 else new_origin = mus_format("vct( 0 ) %" print_mus_long " snd chn %s to -mix-%d", bg, S_mix_vct, mix_infos_ctr);
3469 }
3470 #endif
3471 #if HAVE_SCHEME
3472 new_origin = mus_format("(varlet -env- '-mix-%d (%s %" print_mus_long " snd chn))", mix_infos_ctr, edname, bg);
3473 #endif
3474 #if HAVE_RUBY
3475 /* mix_vct(vct(0.1, 0.2, 0.3), 100, snd, chn, true, "mix_vct(vct(0.1, 0.2, 0.3)") */
3476 new_origin = mus_format("_mix_%d = %s, %" print_mus_long ", snd, chn)", mix_infos_ctr, edname, bg);
3477 #endif
3478
3479 mix_id = mix_buffer_with_tag(cp, data, bg, len, new_origin);
3480 free(new_origin);
3481 }
3482
3483 update_graph(cp);
3484
3485 return(new_xen_mix(mix_id));
3486 }
3487
3488
g_mix(Xen file,Xen chn_samp_n,Xen file_chn,Xen snd_n,Xen chn_n,Xen tag,Xen auto_delete)3489 static Xen g_mix(Xen file, Xen chn_samp_n, Xen file_chn, Xen snd_n, Xen chn_n, Xen tag, Xen auto_delete)
3490 {
3491 #define H_mix "(" S_mix " file :optional (beg 0) (in-chan 0) snd chn (with-tag " S_with_mix_tags ") auto-delete): \
3492 mix channel in-chan of file into snd's channel chn starting at beg (in the output), returning a list of the new mixes. \
3493 If in-chan is " PROC_TRUE ", all input channels are mixed into successive channels of snd, starting at chn. \
3494 if with-tag is " PROC_FALSE ", no draggable tag is created. If \
3495 auto-delete is " PROC_TRUE ", the input file is deleted when it is no longer needed."
3496
3497 chan_info *cp = NULL;
3498 static char *name = NULL;
3499 int chans, id = NO_MIX_TAG, file_channel = 0;
3500 bool with_mixer;
3501 mus_long_t beg = 0, len = 0;
3502
3503 /* it might be nice to make mix generic: vct=mix-vct, region=mix-region, sound-data=mix-sound-data
3504 * also mix-sound|mix-channel (sound object/int), but arg order is confusing (file-chn...)
3505 * and mix-vct has "origin", also file_chn might be env: pan-mix-* [vector list?]
3506 *
3507 * mix-vct origin arg is not used (externally) except as a comment
3508 *
3509 * mix object :channel :out-channel :start (:end?) :with-tag :auto-delete (:edit-position?) (:channels?) :origin
3510 * or
3511 * mix object :start :end :channel :edit-position :out-channel :with-tag :auto-delete :origin ?
3512 * mix in-object out-object :start :end :channel :edit-position :out-channel :with-tag :auto-delete :origin ?
3513 * from
3514 * play object :start :end :channel :edit-position :out-channel :with-sync :wait :stop):
3515 * save_sound_as :file :sound :srate :sample-type :header-type :channel :edit-position :comment):
3516 */
3517
3518 Xen_check_type(Xen_is_string(file), file, 1, S_mix, "a string");
3519 Xen_check_type(Xen_is_number_or_unbound(chn_samp_n), chn_samp_n, 2, S_mix, "an integer");
3520 Xen_check_type(Xen_is_integer_boolean_or_unbound(file_chn), file_chn, 3, S_mix, "an integer or " PROC_TRUE);
3521 Snd_assert_channel(S_mix, snd_n, chn_n, 4);
3522 Xen_check_type(Xen_is_boolean_or_unbound(tag), tag, 6, S_mix, "a boolean");
3523 Xen_check_type(Xen_is_integer_boolean_or_unbound(auto_delete), auto_delete, 7, S_mix, "a boolean or an integer");
3524 if (name) free(name);
3525
3526 name = mus_expand_filename(Xen_string_to_C_string(file));
3527 if (!(mus_file_probe(name)))
3528 return(snd_no_such_file_error(S_mix, file));
3529
3530 cp = get_cp(snd_n, chn_n, S_mix);
3531 if (!cp) return(Xen_false);
3532
3533 if (Xen_is_llong(chn_samp_n))
3534 {
3535 beg = Xen_llong_to_C_llong(chn_samp_n);
3536 if ((beg < 0) ||
3537 (beg > 4294967296LL))
3538 Xen_out_of_range_error(S_mix, 2, chn_samp_n, "begin time should be reasonable");
3539 }
3540 chans = mus_sound_chans(name);
3541 if (chans <= 0)
3542 {
3543 Xen_error(BAD_HEADER,
3544 Xen_list_3(C_string_to_Xen_string(S_mix ": ~S chans <= 0? (~A)"),
3545 file,
3546 C_int_to_Xen_integer(chans)));
3547 }
3548 if (Xen_is_integer(file_chn))
3549 {
3550 file_channel = Xen_integer_to_C_int(file_chn);
3551 if ((file_channel >= chans) ||
3552 (file_channel < 0))
3553 {
3554 Xen_error(NO_SUCH_CHANNEL,
3555 Xen_list_4(C_string_to_Xen_string(S_mix ": chan: ~A, but ~S chans: ~A"),
3556 file_chn,
3557 file,
3558 C_int_to_Xen_integer(chans)));
3559 }
3560 }
3561 else
3562 {
3563 if (Xen_is_true(file_chn)) /* this used to be the default as well -- !Xen_is_bound case */
3564 {
3565 int i, out_chans = 1;
3566 Xen result = Xen_empty_list;
3567 file_delete_t delete_choice = DONT_DELETE_ME;
3568
3569 if (Xen_is_integer(auto_delete))
3570 delete_choice = (file_delete_t)Xen_integer_to_C_int(auto_delete);
3571 else
3572 {
3573 if (Xen_is_boolean(auto_delete))
3574 {
3575 if (Xen_boolean_to_C_bool(auto_delete))
3576 {
3577 if ((chans > 1) &&
3578 (cp->sound->nchans > 1)) /* mix_complete_file sets sync locally so all we care about here is nchans */
3579 {
3580 remember_temp(name, chans);
3581 delete_choice = MULTICHANNEL_DELETION;
3582 }
3583 else delete_choice = DELETE_ME;
3584 }
3585 else delete_choice = DONT_DELETE_ME;
3586 }
3587 }
3588
3589 id = mix_complete_file(cp->sound, beg, name,
3590 (!Xen_is_bound(tag)) ? with_mix_tags(ss) : Xen_boolean_to_C_bool(tag),
3591 delete_choice, MIX_SETS_SYNC_LOCALLY,
3592 &out_chans);
3593 if (id == -1) return(Xen_false);
3594
3595 for (i = 0; i < out_chans; i++)
3596 result = Xen_cons(new_xen_mix(id + i), result);
3597 return(Xen_list_reverse(result));
3598 }
3599 }
3600
3601 len = mus_sound_framples(name);
3602 if (len <= 0) return(Xen_false);
3603
3604 with_mixer = virtual_mix_ok(cp, cp->edit_ctr);
3605 if (with_mixer)
3606 {
3607 if (!Xen_is_bound(tag))
3608 with_mixer = with_mix_tags(ss);
3609 else with_mixer = Xen_boolean_to_C_bool(tag);
3610 }
3611
3612 {
3613 file_delete_t delete_file;
3614
3615 delete_file = xen_to_file_delete_t(auto_delete, S_mix);
3616 if ((delete_file == MULTICHANNEL_DELETION) || (delete_file == MULTICHANNEL_DELETION_IF_FILE))
3617 remember_temp(name, chans);
3618
3619 if (!with_mixer)
3620 {
3621 char *origin;
3622 origin = untagged_mix_to_string(name, beg, file_channel, delete_file == DELETE_ME);
3623 mix_file_untagged(name, file_channel, cp, beg, len, delete_file, origin);
3624 free(origin);
3625 }
3626 else
3627 {
3628 char *origin;
3629 origin = tagged_mix_to_string(name, beg, file_channel, delete_file == DELETE_ME);
3630
3631 if (len < FILE_BUFFER_SIZE)
3632 {
3633 mus_float_t *data;
3634
3635 data = (mus_float_t *)malloc(len * sizeof(mus_float_t));
3636 len = mus_file_to_array(name, file_channel, 0, len, data);
3637 if (len > 0)
3638 id = mix_buffer_with_tag(cp, data, beg, len, origin);
3639 free(data);
3640
3641 if (delete_file == DELETE_ME)
3642 snd_remove(name, REMOVE_FROM_CACHE);
3643 else
3644 {
3645 if (delete_file == MULTICHANNEL_DELETION_IF_FILE)
3646 forget_temp(name, file_channel);
3647 }
3648 }
3649 else
3650 {
3651 id = mix_file_with_tag(cp, name, file_channel, beg, delete_file, origin);
3652 if (mix_exists(id)) /* if edit is blocked, mix_file can return NO_MIX_TAG (-1) */
3653 {
3654 mix_info *md;
3655 md = md_from_id(id);
3656 if (!md->in_filename)
3657 {
3658 md->in_filename = mus_strdup(name);
3659 md->in_chan = file_channel;
3660 }
3661 }
3662 }
3663 free(origin);
3664 }
3665 }
3666
3667 update_graph(cp);
3668
3669 if (id == NO_MIX_TAG) return(Xen_false);
3670 return(Xen_list_1(new_xen_mix(id)));
3671 }
3672
3673
3674
3675 /* ---------------- mix samplers ---------------- */
3676
3677 typedef struct mix_fd {
3678 mix_info *md;
3679 snd_fd *sf;
3680 } mix_fd;
3681
3682 static Xen_object_type_t mf_tag;
3683
is_mix_sampler(Xen obj)3684 bool is_mix_sampler(Xen obj)
3685 {
3686 return(Xen_c_object_is_type(obj, mf_tag));
3687 }
3688
3689 #define Xen_to_mix_sampler(obj) ((mix_fd *)Xen_object_ref(obj))
3690
3691
g_is_mix_sampler(Xen obj)3692 static Xen g_is_mix_sampler(Xen obj)
3693 {
3694 #define H_is_mix_sampler "(" S_is_mix_sampler " obj): " PROC_TRUE " if obj is a mix-sampler"
3695 return(C_bool_to_Xen_boolean(is_mix_sampler(obj)));
3696 }
3697
3698
mix_sampler_to_string(mix_fd * fd)3699 static char *mix_sampler_to_string(mix_fd *fd)
3700 {
3701 char *desc;
3702 desc = (char *)calloc(PRINT_BUFFER_SIZE, sizeof(char));
3703 if ((!fd) || (!fd->sf))
3704 snprintf(desc, PRINT_BUFFER_SIZE, "#<mix-sampler: null>");
3705 else
3706 {
3707 if ((mix_is_active(fd->sf->region)) &&
3708 (fd->md) &&
3709 (fd->sf->region == (fd->md->id)))
3710 {
3711 mix_info *md;
3712 md = fd->md;
3713 snprintf(desc, PRINT_BUFFER_SIZE, "#<mix-sampler mix %d, (from %" print_mus_long ", at %" print_mus_long "%s): %s>",
3714 md->id,
3715 fd->sf->initial_samp,
3716 fd->sf->loc,
3717 (fd->sf->at_eof) ? ", at eof" : "",
3718 (md->in_filename) ? md->in_filename : S_vct);
3719 }
3720 else snprintf(desc, PRINT_BUFFER_SIZE, "#<mix-sampler: inactive>");
3721 }
3722 return(desc);
3723 }
3724
3725 #if HAVE_FORTH || HAVE_RUBY
Xen_wrap_print(mix_fd,print_mf,mix_sampler_to_string)3726 Xen_wrap_print(mix_fd, print_mf, mix_sampler_to_string)
3727 #endif
3728
3729 static void mf_free(mix_fd *fd)
3730 {
3731 if (fd)
3732 {
3733 if (fd->sf)
3734 free_snd_fd(fd->sf);
3735 fd->sf = NULL;
3736 fd->md = NULL;
3737 free(fd);
3738 }
3739 }
3740
3741 #if (!HAVE_SCHEME)
Xen_wrap_free(mix_fd,free_mf,mf_free)3742 Xen_wrap_free(mix_fd, free_mf, mf_free)
3743
3744 #else
3745 static s7_pointer s7_mf_free(s7_scheme *sc, s7_pointer obj)
3746 {
3747 mf_free((mix_fd *)s7_c_object_value(obj));
3748 return(NULL);
3749 }
3750
3751 static s7_pointer s7_mf_is_equal(s7_scheme *sc, s7_pointer args)
3752 {
3753 return(s7_make_boolean(sc, s7_car(args) == s7_cadr(args)));
3754 }
3755
3756 static s7_pointer g_mix_sampler_to_string(s7_scheme *sc, s7_pointer args)
3757 {
3758 char *str;
3759 s7_pointer result;
3760 str = mix_sampler_to_string(Xen_to_mix_sampler(s7_car(args)));
3761 result = s7_make_string(sc, str);
3762 free(str);
3763 return(result);
3764 }
3765 #endif
3766
3767
3768 Xen g_make_mix_sampler(Xen mix_id, Xen ubeg)
3769 {
3770 #define H_make_mix_sampler "(" S_make_mix_sampler " id :optional (beg 0)): return a reader ready to access mix id"
3771 mix_info *md = NULL;
3772 mix_state *ms;
3773 mus_long_t beg;
3774
3775 Xen_check_type(xen_is_mix(mix_id), mix_id, 1, S_make_mix_sampler, "a mix");
3776 Snd_assert_sample_type(S_make_mix_sampler, ubeg, 2);
3777
3778 md = md_from_id(Xen_mix_to_C_int(mix_id));
3779 if (!md)
3780 return(snd_no_such_mix_error(S_make_mix_sampler, mix_id));
3781 beg = beg_to_sample(ubeg, S_make_mix_sampler);
3782
3783 ms = current_mix_state(md);
3784 if (ms)
3785 {
3786 mix_fd *mf;
3787 mf = (mix_fd *)calloc(1, sizeof(mix_fd));
3788 mf->md = md;
3789 mf->sf = make_virtual_mix_reader(md->cp, beg, ms->len, ms->index, ms->scaler, READ_FORWARD);
3790 if (mf->sf)
3791 {
3792 mf->sf->region = md->id;
3793 #if HAVE_SCHEME
3794 {
3795 s7_pointer m;
3796 m = Xen_make_object(mf_tag, mf, 0, free_mf);
3797 s7_c_object_set_let(s7, m, g_mix_sampler_methods);
3798 return(m);
3799 }
3800 #else
3801 return(Xen_make_object(mf_tag, mf, 0, free_mf));
3802 #endif
3803 }
3804 free(mf);
3805 }
3806 return(Xen_false);
3807 }
3808
3809
g_read_mix_sample(Xen obj)3810 static Xen g_read_mix_sample(Xen obj)
3811 {
3812 mix_fd *mf;
3813 #define H_read_mix_sample "(" S_read_mix_sample " reader): read sample from mix reader"
3814 Xen_check_type(is_mix_sampler(obj), obj, 1, S_read_mix_sample, "a mix-sampler");
3815 mf = Xen_to_mix_sampler(obj);
3816 return(C_double_to_Xen_real(read_sample(mf->sf)));
3817 }
3818
3819
xen_mix_to_snd_fd(Xen obj)3820 snd_fd *xen_mix_to_snd_fd(Xen obj)
3821 {
3822 mix_fd *mf;
3823 mf = Xen_to_mix_sampler(obj);
3824 return(mf->sf);
3825 }
3826
mf_to_snd_fd(void * p)3827 snd_fd *mf_to_snd_fd(void *p) {return(((mix_fd *)p)->sf);}
3828
3829
3830 #if HAVE_SCHEME
s7_read_mix_sample(s7_scheme * sc,Xen args)3831 static Xen s7_read_mix_sample(s7_scheme *sc, Xen args)
3832 {
3833 return(g_read_mix_sample(s7_car(args)));
3834 }
3835 #endif
3836
3837
g_copy_mix_sampler(Xen obj)3838 Xen g_copy_mix_sampler(Xen obj)
3839 {
3840 mix_fd *mf;
3841 mf = Xen_to_mix_sampler(obj);
3842 return(g_make_mix_sampler(new_xen_mix(mf->md->id), C_llong_to_Xen_llong(current_location(mf->sf))));
3843 }
3844
3845
g_mix_sampler_home(Xen obj)3846 Xen g_mix_sampler_home(Xen obj)
3847 {
3848 mix_fd *mf;
3849 mf = Xen_to_mix_sampler(obj);
3850 return(new_xen_mix(mf->md->id));
3851 }
3852
3853
mix_sampler_is_at_end(void * ptr)3854 static bool mix_sampler_is_at_end(void *ptr)
3855 {
3856 mix_fd *mf = (mix_fd *)ptr;
3857 return(mf->sf->at_eof);
3858 }
3859
3860
g_mix_sampler_is_at_end(Xen obj)3861 Xen g_mix_sampler_is_at_end(Xen obj)
3862 {
3863 mix_fd *mf;
3864 mf = Xen_to_mix_sampler(obj);
3865 return(C_bool_to_Xen_boolean(mix_sampler_is_at_end(mf)));
3866 }
3867
3868
g_mix_sampler_position(Xen obj)3869 Xen g_mix_sampler_position(Xen obj)
3870 {
3871 mix_fd *mf;
3872 mf = Xen_to_mix_sampler(obj);
3873 if (mix_sampler_is_at_end(mf)) return(Xen_integer_zero);
3874 return(C_llong_to_Xen_llong(current_location(mf->sf)));
3875 }
3876
3877
g_free_mix_sampler(Xen obj)3878 Xen g_free_mix_sampler(Xen obj)
3879 {
3880 mix_fd *mf;
3881 mf = Xen_to_mix_sampler(obj);
3882
3883 if (mf)
3884 {
3885 if (mf->sf)
3886 mf->sf = free_snd_fd(mf->sf);
3887 mf->md = NULL;
3888 }
3889 return(Xen_false);
3890 }
3891
3892
save_mix(int id,const char * name,mus_header_t head_type,mus_sample_t samp_type)3893 static io_error_t save_mix(int id, const char *name, mus_header_t head_type, mus_sample_t samp_type)
3894 {
3895 mix_info *md;
3896 chan_info *cp;
3897 snd_info *sp;
3898 mix_state *ms;
3899 io_error_t io_err;
3900 mus_long_t framples;
3901
3902 md = md_from_id(id);
3903 cp = md->cp;
3904 sp = cp->sound;
3905 ms = current_mix_state(md);
3906 framples = ms->len;
3907
3908 io_err = snd_write_header(name, head_type, snd_srate(sp), 1, framples, samp_type, NULL, NULL);
3909
3910 if (io_err == IO_NO_ERROR)
3911 {
3912 mus_long_t oloc;
3913 int ofd;
3914 oloc = mus_header_data_location();
3915 ofd = snd_reopen_write(name);
3916 if (ofd != -1)
3917 {
3918 mus_float_t **bufs;
3919 mus_float_t *data;
3920 mus_long_t i;
3921 mix_fd *mf;
3922
3923 snd_file_open_descriptors(ofd, name, samp_type, oloc, 1, head_type);
3924 mus_file_set_clipping(ofd, clipping(ss));
3925 lseek(ofd, oloc, SEEK_SET);
3926
3927 mf = (mix_fd *)calloc(1, sizeof(mix_fd));
3928 mf->md = md;
3929 mf->sf = make_virtual_mix_reader(md->cp, 0, ms->len, ms->index, ms->scaler, READ_FORWARD);
3930 mf->sf->region = md->id;
3931
3932 bufs = (mus_float_t **)calloc(1, sizeof(mus_float_t *));
3933 bufs[0] = (mus_float_t *)calloc(FILE_BUFFER_SIZE, sizeof(mus_float_t));
3934 data = bufs[0];
3935
3936 for (i = 0; i < framples; i += FILE_BUFFER_SIZE)
3937 {
3938 int err;
3939 int cursamples, k;
3940 if ((i + FILE_BUFFER_SIZE) < framples)
3941 cursamples = FILE_BUFFER_SIZE;
3942 else cursamples = (framples - i);
3943
3944 for (k = 0; k < cursamples; k++)
3945 data[k] = read_sample(mf->sf);
3946 err = mus_file_write(ofd, 0, cursamples - 1, 1, bufs);
3947 if (err != MUS_NO_ERROR)
3948 {
3949 snd_warning("write error while saving mix");
3950 break;
3951 }
3952 }
3953
3954 free_snd_fd(mf->sf);
3955 free(mf);
3956 free(bufs[0]);
3957 data = NULL;
3958 free(bufs);
3959
3960 mus_file_close(ofd);
3961 }
3962 else snd_error("%s %d in %s: %s", S_save_mix, id, name, snd_io_strerror());
3963 }
3964 else snd_error("%s %d in %s: %s", S_save_mix, id, name, snd_io_strerror());
3965 return(io_err);
3966 }
3967
3968 #define MIX_TAG_Y_SEPARATION 20
3969
copy_mix(int id)3970 int copy_mix(int id)
3971 {
3972 int new_id;
3973 mus_long_t pos;
3974 char *filename, *origin;
3975 mix_info *md, *new_md;
3976
3977 md = md_from_id(id);
3978 if (!md) return(-1);
3979
3980 filename = snd_tempnam();
3981 save_mix(id, filename, MUS_NEXT, MUS_OUT_SAMPLE_TYPE);
3982
3983 pos = mix_position_from_id(id);
3984 origin = tagged_mix_to_string(filename, pos, 0, true); /* true = file should be auto-deleted, I think */
3985
3986 new_id = mix_file_with_tag(md->cp, filename, 0, pos, DELETE_ME, origin);
3987
3988 new_md = md_from_id(new_id);
3989 if (!new_md->in_filename)
3990 new_md->in_filename = mus_strdup(filename);
3991
3992 new_md->tag_y = md->tag_y + MIX_TAG_Y_SEPARATION;
3993
3994 free(origin);
3995 free(filename);
3996 return(new_id);
3997 }
3998
3999
g_save_mix(Xen m,Xen file)4000 static Xen g_save_mix(Xen m, Xen file)
4001 {
4002 #define H_save_mix "(" S_save_mix " mix filename) saves mix's samples in the file 'filename'"
4003 int id;
4004
4005 Xen_check_type(xen_is_mix(m), m, 1, S_save_mix, "a mix");
4006 id = Xen_mix_to_C_int(m);
4007
4008 if (mix_is_active(id))
4009 {
4010 Xen_check_type(Xen_is_string(file), file, 2, S_save_mix, "a filename");
4011 save_mix(id, Xen_string_to_C_string(file), MUS_NEXT, MUS_OUT_SAMPLE_TYPE);
4012 return(m);
4013 }
4014 return(snd_no_such_mix_error(S_save_mix, m));
4015 }
4016
4017
g_view_mixes_dialog(void)4018 static Xen g_view_mixes_dialog(void)
4019 {
4020 #define H_view_mixes_dialog "(" S_view_mixes_dialog "): start the Mix browser"
4021 return(Xen_wrap_widget(make_mix_dialog()));
4022 }
4023
4024
g_mix_dialog_mix(void)4025 static Xen g_mix_dialog_mix(void)
4026 {
4027 #define H_mix_dialog_mix "(" S_mix_dialog_mix "): current mix id displayed in mix dialog."
4028 return(new_xen_mix(mix_dialog_mix()));
4029 }
4030
4031
g_set_mix_dialog_mix(Xen val)4032 static Xen g_set_mix_dialog_mix(Xen val)
4033 {
4034 Xen_check_type(xen_is_mix(val), val, 1, S_set S_mix_dialog_mix, "a mix");
4035 mix_dialog_set_mix(Xen_mix_to_C_int(val));
4036 return(val);
4037 }
4038
4039
play_mix(mix_info * md,mus_long_t beg,bool start_playing)4040 static bool play_mix(mix_info *md, mus_long_t beg, bool start_playing)
4041 {
4042 mix_state *ms;
4043 ms = current_mix_state(md);
4044 if (!ms)
4045 {
4046 int i;
4047 for (i = md->cp->edit_ctr - 1; (!ms) && (i < 0); i--)
4048 ms = ed_mix_state(md->cp->edits[i], md->id);
4049 }
4050 if (ms)
4051 return(add_mix_to_play_list(ms, md->cp, beg, start_playing));
4052 return(false);
4053 }
4054
4055
syncd_mix_play_1(mix_info * md,void * ignore)4056 static void syncd_mix_play_1(mix_info *md, void *ignore)
4057 {
4058 play_mix(md, 0, false);
4059 }
4060
4061
syncd_mix_play(int id)4062 void syncd_mix_play(int id)
4063 {
4064 /* add any syncd mixes to the play list (started later) */
4065 for_each_syncd_mix(id, syncd_mix_play_1, NULL);
4066 }
4067
4068
g_play_mix(Xen num,mus_long_t samp)4069 Xen g_play_mix(Xen num, mus_long_t samp)
4070 {
4071 mix_info *md;
4072
4073 md = md_from_id(Xen_mix_to_C_int(num));
4074 if (!md)
4075 return(snd_no_such_mix_error(S_play, num));
4076 play_mix(md, samp, true);
4077
4078 return(num);
4079 }
4080
4081
play_mix_from_id(int mix_id)4082 bool play_mix_from_id(int mix_id)
4083 {
4084 mix_info *md;
4085 md = md_from_id(mix_id);
4086 if (md)
4087 return(play_mix(md, 0, true));
4088 return(false);
4089 }
4090
4091
4092 #if HAVE_SCHEME
4093 #define S_mix_to_vct "mix->float-vector"
4094 #else
4095 #define S_mix_to_vct "mix->vct"
4096 #endif
4097
g_mix_to_vct(Xen mix_n,Xen beg_n,Xen num)4098 Xen g_mix_to_vct(Xen mix_n, Xen beg_n, Xen num)
4099 {
4100 mus_float_t *data;
4101 mix_info *md;
4102 mus_long_t beg, len;
4103 mus_long_t i;
4104 mix_fd *mf = NULL;
4105 mix_state *ms;
4106
4107 Xen_check_type(xen_is_mix(mix_n), mix_n, 1, S_mix_to_vct, "a mix");
4108 Xen_check_type(Xen_is_integer_or_unbound(beg_n), beg_n, 2, S_mix_to_vct, "an integer");
4109 Xen_check_type(Xen_is_integer_or_unbound(num), num, 3, S_mix_to_vct, "an integer");
4110
4111 md = md_from_id(Xen_mix_to_C_int(mix_n));
4112 if (!md)
4113 return(snd_no_such_mix_error(S_mix_to_vct, mix_n));
4114 ms = current_mix_state(md);
4115
4116 if (Xen_is_integer(num))
4117 {
4118 len = Xen_llong_to_C_llong(num);
4119 if (len < 0)
4120 Xen_out_of_range_error(S_mix_to_vct, 2, num, "length < 0?");
4121 if (len > mix_length_from_id(md->id))
4122 len = mix_length_from_id(md->id);
4123 }
4124 else len = mix_length_from_id(md->id);
4125
4126 beg = beg_to_sample(beg_n, S_mix_to_vct);
4127 if (beg >= len)
4128 return(Xen_false);
4129
4130 mf = (mix_fd *)calloc(1, sizeof(mix_fd));
4131 mf->md = md;
4132 mf->sf = make_virtual_mix_reader(md->cp, beg, ms->len, ms->index, ms->scaler, READ_FORWARD);
4133 mf->sf->region = md->id;
4134
4135 data = (mus_float_t *)calloc(len, sizeof(mus_float_t));
4136 for (i = 0; i < len; i++)
4137 data[i] = read_sample(mf->sf);
4138
4139 free_snd_fd(mf->sf);
4140 free(mf);
4141
4142 return(xen_make_vct(len, data));
4143 }
4144
4145
Xen_wrap_1_arg(g_mix_position_w,g_mix_position)4146 Xen_wrap_1_arg(g_mix_position_w, g_mix_position)
4147 Xen_wrap_2_args(g_set_mix_position_w, g_set_mix_position)
4148 Xen_wrap_1_arg(g_mix_speed_w, g_mix_speed)
4149 Xen_wrap_2_args(g_set_mix_speed_w, g_set_mix_speed)
4150 Xen_wrap_1_arg(g_mix_amp_w, g_mix_amp)
4151 Xen_wrap_2_args(g_set_mix_amp_w, g_set_mix_amp)
4152 Xen_wrap_1_arg(g_mix_amp_env_w, g_mix_amp_env)
4153 Xen_wrap_2_args(g_set_mix_amp_env_w, g_set_mix_amp_env)
4154
4155 Xen_wrap_1_arg(g_mix_name_w, g_mix_name)
4156 Xen_wrap_2_args(g_set_mix_name_w, g_set_mix_name)
4157 Xen_wrap_1_arg(g_mix_tag_y_w, g_mix_tag_y)
4158 Xen_wrap_2_args(g_set_mix_tag_y_w, g_set_mix_tag_y)
4159 Xen_wrap_1_arg(g_mix_sync_w, g_mix_sync)
4160 Xen_wrap_2_args(g_set_mix_sync_w, g_set_mix_sync)
4161 Xen_wrap_no_args(g_mix_sync_max_w, g_mix_sync_max)
4162 Xen_wrap_1_arg(g_mix_length_w, g_mix_length)
4163 Xen_wrap_1_arg(g_integer_to_mix_w, g_integer_to_mix)
4164 Xen_wrap_1_arg(g_mix_to_integer_w, g_mix_to_integer)
4165 Xen_wrap_1_arg(g_mix_home_w, g_mix_home)
4166 Xen_wrap_1_arg(g_mix_properties_w, g_mix_properties)
4167 Xen_wrap_2_args(g_set_mix_properties_w, g_set_mix_properties)
4168 Xen_wrap_2_args(g_mix_property_w, g_mix_property)
4169 Xen_wrap_3_args(g_set_mix_property_w, g_set_mix_property)
4170
4171 Xen_wrap_no_args(g_mix_waveform_height_w, g_mix_waveform_height)
4172 Xen_wrap_1_arg(g_set_mix_waveform_height_w, g_set_mix_waveform_height)
4173 Xen_wrap_no_args(g_with_mix_tags_w, g_with_mix_tags)
4174 Xen_wrap_1_arg(g_set_with_mix_tags_w, g_set_with_mix_tags)
4175 Xen_wrap_no_args(g_mix_tag_width_w, g_mix_tag_width)
4176 Xen_wrap_1_arg(g_set_mix_tag_width_w, g_set_mix_tag_width)
4177 Xen_wrap_no_args(g_mix_tag_height_w, g_mix_tag_height)
4178 Xen_wrap_1_arg(g_set_mix_tag_height_w, g_set_mix_tag_height)
4179
4180 Xen_wrap_1_arg(g_is_mix_w, g_is_mix)
4181 Xen_wrap_2_optional_args(g_mixes_w, g_mixes)
4182 Xen_wrap_7_optional_args(g_mix_w, g_mix)
4183 Xen_wrap_6_optional_args(g_mix_vct_w, g_mix_vct)
4184
4185 Xen_wrap_2_optional_args(g_make_mix_sampler_w, g_make_mix_sampler)
4186 Xen_wrap_1_arg(g_read_mix_sample_w, g_read_mix_sample)
4187 Xen_wrap_1_arg(g_is_mix_sampler_w, g_is_mix_sampler)
4188 Xen_wrap_2_args(g_save_mix_w, g_save_mix)
4189
4190 Xen_wrap_no_args(g_view_mixes_dialog_w, g_view_mixes_dialog)
4191 Xen_wrap_no_args(g_mix_dialog_mix_w, g_mix_dialog_mix)
4192 Xen_wrap_1_arg(g_set_mix_dialog_mix_w, g_set_mix_dialog_mix)
4193
4194 #if HAVE_SCHEME
4195 static s7_pointer acc_mix_tag_height(s7_scheme *sc, s7_pointer args) {return(g_set_mix_tag_height(s7_cadr(args)));}
acc_mix_tag_width(s7_scheme * sc,s7_pointer args)4196 static s7_pointer acc_mix_tag_width(s7_scheme *sc, s7_pointer args) {return(g_set_mix_tag_width(s7_cadr(args)));}
acc_mix_waveform_height(s7_scheme * sc,s7_pointer args)4197 static s7_pointer acc_mix_waveform_height(s7_scheme *sc, s7_pointer args) {return(g_set_mix_waveform_height(s7_cadr(args)));}
acc_with_mix_tags(s7_scheme * sc,s7_pointer args)4198 static s7_pointer acc_with_mix_tags(s7_scheme *sc, s7_pointer args) {return(g_set_with_mix_tags(s7_cadr(args)));}
4199
s7_mix_sampler_to_let(s7_scheme * sc,s7_pointer args)4200 static s7_pointer s7_mix_sampler_to_let(s7_scheme *sc, s7_pointer args)
4201 {
4202 /* this is called upon (object->let <mix-sampler>) */
4203 s7_pointer m, env;
4204 mix_fd *fd;
4205
4206 m = s7_car(args);
4207 env = s7_cadr(args);
4208 fd = Xen_to_mix_sampler(m);
4209
4210 if ((fd) && (fd->sf) &&
4211 (mix_is_active(fd->sf->region)) &&
4212 (fd->md) &&
4213 (fd->sf->region == (fd->md->id)))
4214 {
4215 mix_info *md;
4216 md = fd->md;
4217
4218 s7_varlet(sc, env, s7_make_symbol(sc, "mix"), (mix_is_active(md->id)) ? new_xen_mix(md->id) : Xen_false);
4219 s7_varlet(sc, env, s7_make_symbol(sc, "source"), (md->in_filename) ? s7_make_string(sc, md->in_filename) : Xen_false);
4220 s7_varlet(sc, env, s7_make_symbol(sc, "start"), s7_make_integer(sc, fd->sf->initial_samp));
4221 s7_varlet(sc, env, s7_make_symbol(sc, "position"), s7_make_integer(sc, fd->sf->loc));
4222 s7_varlet(sc, env, s7_make_symbol(sc, "eof"), s7_make_boolean(sc, fd->sf->at_eof));
4223 }
4224 return(env);
4225 }
4226
s7_mix_to_let(s7_scheme * sc,s7_pointer args)4227 static s7_pointer s7_mix_to_let(s7_scheme *sc, s7_pointer args)
4228 {
4229 /* this is called upon (object->let <mix>) */
4230 s7_pointer m, env;
4231 m = s7_car(args);
4232 env = s7_cadr(args);
4233
4234 s7_varlet(sc, env, s7_make_symbol(sc, "position"), g_mix_position(m));
4235 return(env);
4236 }
4237 #endif
4238
g_init_mix(void)4239 void g_init_mix(void)
4240 {
4241 #if HAVE_SCHEME
4242 s7_pointer i, m, b, s, p, t, f, ms, fv;
4243 i = s7_make_symbol(s7, "integer?");
4244 m = s7_make_symbol(s7, "mix?");
4245 ms = s7_make_symbol(s7, "mix-sampler?");
4246 b = s7_make_symbol(s7, "boolean?");
4247 s = s7_make_symbol(s7, "string?");
4248 p = s7_make_symbol(s7, "list?");
4249 f = s7_make_symbol(s7, "float?");
4250 fv = s7_make_symbol(s7, "float-vector?");
4251 t = s7_t(s7);
4252
4253 mix_to_let_func = s7_make_function(s7, "mix->let", s7_mix_to_let, 2, 0, false, "mix->let");
4254 mix_sampler_to_let_func = s7_make_function(s7, "mix-sampler->let", s7_mix_sampler_to_let, 2, 0, false, "mix-sampler->let");
4255 #endif
4256
4257 init_xen_mix();
4258
4259 #if HAVE_SCHEME
4260 g_mix_sampler_methods = s7_openlet(s7, s7_inlet(s7, s7_list(s7, 2, s7_make_symbol(s7, "object->let"), mix_sampler_to_let_func)));
4261 s7_gc_protect(s7, g_mix_sampler_methods);
4262 mf_tag = s7_make_c_type(s7, "<mix-sampler>");
4263 s7_c_type_set_gc_free(s7, mf_tag, s7_mf_free);
4264 s7_c_type_set_is_equal(s7, mf_tag, s7_mf_is_equal);
4265 s7_c_type_set_ref(s7, mf_tag, s7_read_mix_sample);
4266 s7_c_type_set_to_string(s7, mf_tag, g_mix_sampler_to_string);
4267 #else
4268 mf_tag = Xen_make_object_type("MixSampler", sizeof(mix_fd));
4269 #endif
4270
4271 #if HAVE_RUBY
4272 rb_define_method(mf_tag, "to_s", Xen_procedure_cast print_mf, 0);
4273 rb_define_method(mf_tag, "call", Xen_procedure_cast g_read_mix_sample, 0);
4274 #endif
4275
4276 #if HAVE_FORTH
4277 fth_set_object_inspect(mf_tag, print_mf);
4278 fth_set_object_free(mf_tag, free_mf);
4279 fth_set_object_apply(mf_tag, Xen_procedure_cast g_read_mix_sample, 0, 0, 0);
4280 #endif
4281
4282 Xen_define_typed_procedure(S_make_mix_sampler, g_make_mix_sampler_w, 1, 1, 0, H_make_mix_sampler, s7_make_signature(s7, 3, ms, m, i));
4283 Xen_define_typed_procedure(S_read_mix_sample, g_read_mix_sample_w, 1, 0, 0, H_read_mix_sample, s7_make_signature(s7, 2, f, ms));
4284 Xen_define_typed_procedure(S_is_mix_sampler, g_is_mix_sampler_w, 1, 0, 0, H_is_mix_sampler, s7_make_signature(s7, 2, b, t));
4285
4286 Xen_define_typed_procedure(S_save_mix, g_save_mix_w, 2, 0, 0, H_save_mix, s7_make_signature(s7, 3, m, m, s));
4287 Xen_define_typed_procedure(S_mix, g_mix_w, 1, 6, 0, H_mix, s7_make_signature(s7, 8, t, s, i, t, t, t, b, t));
4288 Xen_define_typed_procedure(S_mix_vct, g_mix_vct_w, 1, 5, 0, H_mix_vct, s7_make_signature(s7, 7, m, fv, i, t, t, b, s));
4289
4290 Xen_define_typed_procedure(S_mixes, g_mixes_w, 0, 2, 0, H_mixes, s7_make_signature(s7, 3, p, t, t));
4291 Xen_define_typed_procedure(S_mix_home, g_mix_home_w, 1, 0, 0, H_mix_home, s7_make_signature(s7, 2, p, m));
4292 Xen_define_typed_procedure(S_is_mix, g_is_mix_w, 1, 0, 0, H_is_mix, s7_make_signature(s7, 2, b, t));
4293 Xen_define_typed_procedure(S_mix_length, g_mix_length_w, 1, 0, 0, H_mix_length, s7_make_signature(s7, 2, i, m));
4294 Xen_define_typed_procedure(S_integer_to_mix, g_integer_to_mix_w, 1, 0, 0, H_integer_to_mix, s7_make_signature(s7, 2, m, i));
4295 Xen_define_typed_procedure(S_mix_to_integer, g_mix_to_integer_w, 1, 0, 0, H_mix_to_integer, s7_make_signature(s7, 2, i, m));
4296 Xen_define_typed_procedure(S_view_mixes_dialog, g_view_mixes_dialog_w, 0, 0, 0, H_view_mixes_dialog, s7_make_signature(s7, 1, p));
4297 Xen_define_typed_procedure(S_mix_sync_max, g_mix_sync_max_w, 0, 0, 0, H_mix_sync_max, s7_make_signature(s7, 1, i));
4298
4299 Xen_define_typed_dilambda(S_mix_position, g_mix_position_w, H_mix_position, S_set S_mix_position, g_set_mix_position_w, 1, 0, 2, 0,
4300 s7_make_signature(s7, 2, i, m), s7_make_signature(s7, 3, i, m, i));
4301 Xen_define_typed_dilambda(S_mix_speed, g_mix_speed_w, H_mix_speed, S_set S_mix_speed, g_set_mix_speed_w, 1, 0, 2, 0,
4302 s7_make_signature(s7, 2, f, m), s7_make_signature(s7, 3, f, m, f));
4303 Xen_define_typed_dilambda(S_mix_amp, g_mix_amp_w, H_mix_amp, S_set S_mix_amp, g_set_mix_amp_w, 1, 0, 2, 0,
4304 s7_make_signature(s7, 2, f, m), s7_make_signature(s7, 3, f, m, f));
4305 Xen_define_typed_dilambda(S_mix_amp_env, g_mix_amp_env_w, H_mix_amp_env, S_set S_mix_amp_env, g_set_mix_amp_env_w, 1, 0, 2, 0,
4306 s7_make_signature(s7, 2, p, m), s7_make_signature(s7, 3, p, m, s7_make_signature(s7, 2, p, b)));
4307
4308 Xen_define_typed_dilambda(S_mix_name, g_mix_name_w, H_mix_name, S_set S_mix_name, g_set_mix_name_w, 1, 0, 2, 0,
4309 s7_make_signature(s7, 2, s, m), s7_make_signature(s7, 3, s, m, s));
4310 Xen_define_typed_dilambda(S_mix_sync, g_mix_sync_w, H_mix_sync, S_set S_mix_sync, g_set_mix_sync_w, 1, 0, 2, 0,
4311 s7_make_signature(s7, 2, i, m), s7_make_signature(s7, 3, i, m, s7_make_signature(s7, 2, i, b)));
4312 Xen_define_typed_dilambda(S_mix_properties, g_mix_properties_w, H_mix_properties, S_set S_mix_properties, g_set_mix_properties_w, 1, 0, 2, 0,
4313 s7_make_signature(s7, 2, p, m), s7_make_signature(s7, 3, p, m, p));
4314 Xen_define_typed_dilambda(S_mix_property, g_mix_property_w, H_mix_property, S_set S_mix_property, g_set_mix_property_w, 2, 0, 3, 0,
4315 s7_make_signature(s7, 3, t, t, m), s7_make_circular_signature(s7, 3, 4, t, t, m, t));
4316 Xen_define_typed_dilambda(S_mix_tag_y, g_mix_tag_y_w, H_mix_tag_y, S_set S_mix_tag_y, g_set_mix_tag_y_w, 1, 0, 2, 0,
4317 s7_make_signature(s7, 2, i, m), s7_make_signature(s7, 3, i, m, i));
4318
4319 Xen_define_typed_dilambda(S_mix_tag_width, g_mix_tag_width_w, H_mix_tag_width, S_set S_mix_tag_width, g_set_mix_tag_width_w, 0, 0, 1, 0,
4320 s7_make_signature(s7, 1, i), s7_make_signature(s7, 2, i, i));
4321 Xen_define_typed_dilambda(S_mix_tag_height, g_mix_tag_height_w, H_mix_tag_height, S_set S_mix_tag_height, g_set_mix_tag_height_w, 0, 0, 1, 0,
4322 s7_make_signature(s7, 1, i), s7_make_signature(s7, 2, i, i));
4323
4324 Xen_define_typed_dilambda(S_mix_waveform_height, g_mix_waveform_height_w, H_mix_waveform_height, S_set S_mix_waveform_height, g_set_mix_waveform_height_w, 0, 0, 1, 0,
4325 s7_make_signature(s7, 1, i), s7_make_signature(s7, 2, i, i));
4326 Xen_define_typed_dilambda(S_with_mix_tags, g_with_mix_tags_w, H_with_mix_tags, S_set S_with_mix_tags, g_set_with_mix_tags_w, 0, 0, 1, 0,
4327 s7_make_signature(s7, 1, b), s7_make_signature(s7, 2, b, b));
4328 Xen_define_typed_dilambda(S_mix_dialog_mix, g_mix_dialog_mix_w, H_mix_dialog_mix, S_set S_mix_dialog_mix, g_set_mix_dialog_mix_w, 0, 0, 1, 0,
4329 s7_make_signature(s7, 1, m), s7_make_signature(s7, 2, m, m));
4330
4331 #define H_mix_release_hook S_mix_release_hook " (id samples): called after the mouse has dragged a mix to some new position. \
4332 'samples' = samples moved in the course of the drag. If it returns " PROC_TRUE ", the actual remix is the hook's responsibility."
4333
4334 mix_release_hook = Xen_define_hook(S_mix_release_hook, "(make-hook 'id 'samples)", 2, H_mix_release_hook);
4335
4336 #define H_mix_drag_hook S_mix_drag_hook " (id x y): called when a mix is dragged"
4337
4338 mix_drag_hook = Xen_define_hook(S_mix_drag_hook, "(make-hook 'id 'x 'y)", 3, H_mix_drag_hook); /* args = id, mouse x, mouse or tag y */
4339
4340 /* the name draw-mix-hook is inconsistent with the other mix hooks (mix-draw-hook?), but is intended to parallel draw-mark-hook */
4341 #define H_draw_mix_hook S_draw_mix_hook " (id old-x old-y x y): called when a mix tag is about to be displayed"
4342
4343 draw_mix_hook = Xen_define_hook(S_draw_mix_hook, "(make-hook 'id 'old-x 'old-y 'x 'y)", 5, H_draw_mix_hook);
4344
4345 #if HAVE_SCHEME
4346 s7_set_setter(s7, ss->mix_tag_height_symbol, s7_make_function(s7, "[acc-" S_mix_tag_height "]", acc_mix_tag_height, 2, 0, false, "accessor"));
4347 s7_set_setter(s7, ss->mix_tag_width_symbol, s7_make_function(s7, "[acc-" S_mix_tag_width "]", acc_mix_tag_width, 2, 0, false, "accessor"));
4348 s7_set_setter(s7, ss->mix_waveform_height_symbol, s7_make_function(s7, "[acc-" S_mix_waveform_height "]", acc_mix_waveform_height, 2, 0, false, "accessor"));
4349 s7_set_setter(s7, ss->with_mix_tags_symbol, s7_make_function(s7, "[acc-" S_with_mix_tags "]", acc_with_mix_tags, 2, 0, false, "accessor"));
4350
4351 s7_set_documentation(s7, ss->mix_tag_height_symbol, "*mix-tag-height*: height (pixels) of mix tags (14)");
4352 s7_set_documentation(s7, ss->mix_tag_width_symbol, "*mix-tag-width*: width (pixels) of mix tags (6)");
4353 s7_set_documentation(s7, ss->mix_waveform_height_symbol, "*mix-waveform-height*: max height (pixels) of mix waveforms (20)");
4354 s7_set_documentation(s7, ss->with_mix_tags_symbol, "*with-mix-tags*: #t if Snd should try to use virtual (tagged) mixing");
4355 #endif
4356 }
4357
4358