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