1 #include "snd.h"
2 #include "clm2xen.h"
3 
4 
cp_has_selection(chan_info * cp)5 static bool cp_has_selection(chan_info *cp)
6 {
7   ed_list *ed;
8   ed = cp->edits[cp->edit_ctr];
9   return((ed) && (ed->selection_beg != NO_SELECTION));
10 }
11 
12 
map_over_chans(bool (* func)(chan_info * ncp))13 static bool map_over_chans(bool (*func)(chan_info *ncp))
14 {
15   /* non-zero = abort map, skips inactive sounds */
16   int i;
17   bool val = false;
18 
19   for (i = 0; i < ss->max_sounds; i++)
20     {
21       uint32_t j;
22       snd_info *sp;
23       chan_info *cp;
24       sp = ss->sounds[i];
25       if ((sp) && (sp->inuse == SOUND_NORMAL))
26 	for (j = 0; j < sp->nchans; j++)
27 	  if ((cp = ((chan_info *)(sp->chans[j]))))
28 	    if (cp)
29 	      {
30 		val = (*func)(cp);
31 		if (val) return(val);
32 	      }
33       }
34   return(val);
35 }
36 
37 
selection_is_active(void)38 bool selection_is_active(void)
39 {
40   /* is selection active in any channel */
41   return(map_over_chans(cp_has_selection));
42 }
43 
44 
selection_is_active_in_channel(chan_info * cp)45 bool selection_is_active_in_channel(chan_info *cp)
46 {
47   return((cp) && (cp_has_selection(cp)));
48 }
49 
50 
selection_is_visible(chan_info * cp)51 static bool selection_is_visible(chan_info *cp)
52 {
53   ed_list *ed;
54   axis_info *ap;
55 
56   ed = cp->edits[cp->edit_ctr];
57   if (ed->selection_beg == NO_SELECTION) return(false);
58 
59   ap = cp->axis;
60   return((ed) &&
61 	 (ap->losamp < ed->selection_end) &&
62 	 (ap->hisamp > ed->selection_beg));
63 }
64 
65 
selection_is_visible_in_channel(chan_info * cp)66 bool selection_is_visible_in_channel(chan_info *cp)
67 {
68   return((cp_has_selection(cp)) &&
69 	 (selection_is_visible(cp)));
70 }
71 
72 
mus_long_t_map_over_chans(mus_long_t (* func)(chan_info *,mus_long_t *),mus_long_t * userptr)73 static mus_long_t mus_long_t_map_over_chans(mus_long_t (*func)(chan_info *, mus_long_t *), mus_long_t *userptr)
74 {
75   int i;
76   mus_long_t val = 0;
77 
78   for (i = 0; i < ss->max_sounds; i++)
79     {
80       uint32_t j;
81       snd_info *sp;
82       chan_info *cp;
83 
84       sp = ss->sounds[i];
85       if ((sp) && (sp->inuse == SOUND_NORMAL))
86 	for (j = 0; j < sp->nchans; j++)
87 	  if ((cp = ((chan_info *)(sp->chans[j]))))
88 	    {
89 	      val = (*func)(cp, userptr);
90 	      if (val) return(val);
91 	    }
92     }
93   return(val);
94 }
95 
96 
cp_selection_beg(chan_info * cp,mus_long_t * beg)97 static mus_long_t cp_selection_beg(chan_info *cp, mus_long_t *beg)
98 {
99   ed_list *ed;
100 
101   ed = cp->edits[cp->edit_ctr];
102   if (ed->selection_beg != NO_SELECTION)
103     {
104       beg[0] = ed->selection_beg;
105       return(1); /* i.e. stop map_over_chans */
106     }
107 
108   return(0);
109 }
110 
111 
selection_beg(chan_info * cp)112 mus_long_t selection_beg(chan_info *cp)
113 {
114   mus_long_t beg[1];
115   beg[0] = 0;
116   if (cp)
117     cp_selection_beg(cp, beg);
118   else mus_long_t_map_over_chans(cp_selection_beg, beg);
119   return(beg[0]);
120 }
121 
122 
123 static int xen_selection_counter = 0;
124 
cp_set_selection_beg(chan_info * cp,mus_long_t beg)125 static void cp_set_selection_beg(chan_info *cp, mus_long_t beg)
126 {
127   ed_list *ed;
128   mus_long_t len;
129 
130   ed = cp->edits[cp->edit_ctr];
131   len = current_samples(cp);
132   if (beg < len)
133     ed->selection_beg = beg;
134   else ed->selection_beg = len - 1;
135   xen_selection_counter++;
136 
137   ed->selection_maxamp = -1.0;
138   ed->selection_maxamp_position = -1;
139 }
140 
141 
selection_end(chan_info * cp)142 mus_long_t selection_end(chan_info *cp) /* never called without selection_member check in advance */
143 {
144   return(cp->edits[cp->edit_ctr]->selection_end);
145 }
146 
147 
cp_selection_len(chan_info * cp,mus_long_t * ptr)148 static mus_long_t cp_selection_len(chan_info *cp, mus_long_t *ptr)
149 {
150   ed_list *ed;
151   ed = cp->edits[cp->edit_ctr];
152   if ((ed) && (ed->selection_beg != NO_SELECTION))
153     return(ed->selection_end - ed->selection_beg + 1);
154   return(0);
155 }
156 
157 
selection_len(void)158 mus_long_t selection_len(void)
159 {
160   return(mus_long_t_map_over_chans(cp_selection_len, NULL));
161 }
162 
163 
cp_set_selection_len(chan_info * cp,mus_long_t len)164 static void cp_set_selection_len(chan_info *cp, mus_long_t len)
165 {
166   ed_list *ed;
167   mus_long_t cplen;
168   ed = cp->edits[cp->edit_ctr];
169   cplen = current_samples(cp);
170   ed->selection_end = ed->selection_beg + len - 1;
171   if (ed->selection_end >= cplen) ed->selection_end = cplen - 1;
172   ed->selection_maxamp = -1.0;
173   ed->selection_maxamp_position = -1;
174 }
175 
176 
selection_chans_1(chan_info * cp,int * counter)177 static void selection_chans_1(chan_info *cp, int *counter)
178 {
179   if (cp_has_selection(cp)) counter[0]++;
180 }
181 
182 
selection_chans(void)183 int selection_chans(void)
184 {
185   int count[1];
186   count[0] = 0;
187   for_each_normal_chan_with_refint(selection_chans_1, count);
188   return(count[0]);
189 }
190 
191 
selection_srate_1(chan_info * cp,mus_long_t * ignored)192 static mus_long_t selection_srate_1(chan_info *cp, mus_long_t *ignored)
193 {
194   if (cp_has_selection(cp))
195     return((mus_long_t)snd_srate(cp->sound));
196   return(0);
197 }
198 
199 
selection_srate(void)200 int selection_srate(void)
201 {
202   if (selection_is_active())
203     return((int)mus_long_t_map_over_chans(selection_srate_1, NULL));
204   return(0);
205 }
206 
207 
selection_maxamp(chan_info * cp)208 mus_float_t selection_maxamp(chan_info *cp)
209 {
210   mus_float_t val = 0.0;
211   if (selection_is_active_in_channel(cp))
212     {
213       mus_long_t maxpos = 0;
214       val = ed_selection_maxamp(cp);
215       if (val >= 0.0) return(val);
216       val = channel_local_maxamp(cp,
217 				 selection_beg(cp),
218 				 selection_end(cp) - selection_beg(cp) + 1,
219 				 cp->edit_ctr,
220 				 &maxpos);
221       set_ed_selection_maxamp(cp, val);
222       set_ed_selection_maxamp_position(cp, maxpos);
223     }
224   return(val);
225 }
226 
227 
selection_maxamp_position(chan_info * cp)228 static mus_long_t selection_maxamp_position(chan_info *cp)
229 {
230   selection_maxamp(cp);
231   return(ed_selection_maxamp_position(cp));
232 }
233 
234 
cp_delete_selection(chan_info * cp)235 void cp_delete_selection(chan_info *cp)
236 {
237   ed_list *ed;
238   ed = cp->edits[cp->edit_ctr];
239   if ((ed) && (ed->selection_beg != NO_SELECTION))
240     {
241       delete_samples(ed->selection_beg, cp_selection_len(cp, NULL), cp, cp->edit_ctr);
242       ed = cp->edits[cp->edit_ctr];
243       ed->selection_beg = NO_SELECTION;
244     }
245 }
246 
247 
delete_selection(cut_selection_regraph_t regraph)248 bool delete_selection(cut_selection_regraph_t regraph)
249 {
250   if (selection_is_active())
251     {
252       for_each_normal_chan(cp_delete_selection);
253       if (regraph == UPDATE_DISPLAY)
254 	for_each_normal_chan(update_graph);
255       enved_reflect_selection(false);
256 
257       if (Xen_hook_has_list(ss->effects_hook))
258 	run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
259 
260       return(true);
261     }
262   return(false);
263 }
264 
265 
cp_deactivate_selection(chan_info * cp)266 static void cp_deactivate_selection(chan_info *cp)
267 {
268   ed_list *ed;
269   ed = cp->edits[cp->edit_ctr];
270   if (ed) ed->selection_beg = NO_SELECTION;
271 }
272 
273 
274 static sync_info *syncd_chans = NULL;
275 
deactivate_selection(void)276 void deactivate_selection(void)
277 {
278   for_each_normal_chan(cp_deactivate_selection);
279   for_each_normal_chan(update_graph);
280   enved_reflect_selection(false);
281 
282   if (Xen_hook_has_list(ss->effects_hook))
283     run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
284 
285   if (syncd_chans)
286     syncd_chans = free_sync_info(syncd_chans);
287 }
288 
289 
reactivate_selection(chan_info * cp,mus_long_t beg,mus_long_t end)290 void reactivate_selection(chan_info *cp, mus_long_t beg, mus_long_t end)
291 {
292   ed_list *ed;
293   mus_long_t len;
294 
295   ed = cp->edits[cp->edit_ctr];
296   len = current_samples(cp) - 1;
297   if (beg < 0) beg = 0;
298   if (end < 0) end = 0;
299   if (beg > len) beg = len;
300   if (end > len) end = len;
301   if (beg > end) end = beg;
302 
303   ed->selection_beg = beg;
304   ed->selection_end = end;
305   cp->selection_visible = false;
306   ed->selection_maxamp = -1.0;
307   ed->selection_maxamp_position = -1;
308   xen_selection_counter++;
309   enved_reflect_selection(true);
310   reflect_selection_in_save_as_dialog(true);
311 
312   if (Xen_hook_has_list(ss->effects_hook))
313     run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
314 }
315 
316 
ripple_selection(ed_list * ed,mus_long_t beg,mus_long_t num)317 void ripple_selection(ed_list *ed, mus_long_t beg, mus_long_t num)
318 {
319   /* beg = insert or delete begin point (snd-edits.c), num = samps inserted (num positive) or deleted (num negative) at beg */
320   if (ed->selection_beg != NO_SELECTION)
321     {
322       if (beg < ed->selection_beg)
323 	{
324 	  ed->selection_beg += num;
325 	  if (beg >= ed->selection_beg)
326 	    ed->selection_beg = NO_SELECTION; /* deletion included some of current selection from outside */
327 	  else ed->selection_end += num;
328 	}
329       else
330 	{
331 	  if (beg < ed->selection_end)
332 	    {
333 	      ed->selection_end += num;
334 	      if (ed->selection_end < beg)
335 		ed->selection_beg = NO_SELECTION; /* same as above but from end */
336 	    }
337 	}
338     }
339 }
340 
341 
next_selection_chan(chan_info * cp,void * sidata)342 static void next_selection_chan(chan_info *cp, void *sidata)
343 {
344   if (cp_has_selection(cp))
345     {
346       sync_info *si = (sync_info *)sidata;
347       int chan;
348       chan = si->chans;
349       si->chans++;
350       si->begs[chan] = selection_beg(cp);
351       si->cps[chan] = cp;
352     }
353 }
354 
355 
selection_sync(void)356 sync_info *selection_sync(void)
357 {
358   sync_info *si;
359   if (!(selection_is_active())) return(NULL);
360   si = (sync_info *)calloc(1, sizeof(sync_info));
361   si->chans = selection_chans();
362   si->cps = (chan_info **)calloc(si->chans, sizeof(chan_info *));
363   si->begs = (mus_long_t *)calloc(si->chans, sizeof(mus_long_t));
364   si->chans = 0;
365   for_each_normal_chan_with_void(next_selection_chan, (void *)si);
366   return(si);
367 }
368 
369 
mix_selection(chan_info * cp,sync_info * si_out,mus_long_t beg,io_error_t * err,int start_chan)370 static int mix_selection(chan_info *cp, sync_info *si_out, mus_long_t beg, io_error_t *err, int start_chan)
371 {
372   char *tempfile;
373   int id = INVALID_MIX_ID;
374   io_error_t io_err;
375 
376   tempfile = snd_tempnam();
377   io_err = save_selection(tempfile, snd_srate(cp->sound), MUS_OUT_SAMPLE_TYPE, MUS_NEXT, NULL, SAVE_ALL_CHANS);
378   if (io_err == IO_NO_ERROR)
379     {
380       char *origin = NULL;
381 #if HAVE_FORTH
382       origin = mus_format("%" print_mus_long " snd chn %s", beg, S_mix_selection);
383 #else
384   #if HAVE_SCHEME
385       origin = mus_format("-mix-selection- %" print_mus_long " %" print_mus_long " %" print_mus_long " snd chn",
386                           beg, selection_beg(cp), selection_len());
387   #else
388       origin = mus_format("%s" PROC_OPEN "%" print_mus_long, to_proc_name(S_mix_selection), beg);
389   #endif
390 #endif
391       if (si_out->chans > 1)
392 	remember_temp(tempfile, si_out->chans);
393 
394       id = mix_file(beg, selection_len(), si_out->chans, si_out->cps, tempfile,
395 		    (si_out->chans > 1) ? MULTICHANNEL_DELETION : DELETE_ME,
396 		    origin, with_mix_tags(ss),
397 		    start_chan);
398       free(origin);
399     }
400   if (tempfile) free(tempfile);
401   (*err) = io_err;
402 
403   return(id);
404 }
405 
406 
add_selection_or_region(int reg,chan_info * cp)407 void add_selection_or_region(int reg, chan_info *cp)
408 {
409   /* in all cases, this has a local sound to report in (kbd, xmenu) */
410   if (cp)
411     {
412       if (is_editable(cp))
413 	{
414 	  io_error_t io_err = IO_NO_ERROR;
415 	  bool got_selection;
416 	  got_selection = ((reg == 0) && (selection_is_active()));
417 	  if (got_selection)
418 	    {
419 	      sync_info *si_out;
420 	      si_out = sync_to_chan(cp);
421 	      mix_selection(cp, si_out, cursor_sample(cp), &io_err, 0);
422 	      free_sync_info(si_out);
423 	    }
424 	  else io_err = add_region(reg, cp);
425 	  if (io_err != IO_NO_ERROR)
426 	    status_report(cp->sound, "can't mix %s: %s",
427 				 (got_selection) ? "selection" : "region",
428 				 io_error_name(io_err));
429 	}
430     }
431   else snd_error_without_format("no channel to mix into?");
432 }
433 
434 
insert_selection(chan_info * cp,sync_info * si_out,mus_long_t beg)435 static io_error_t insert_selection(chan_info *cp, sync_info *si_out, mus_long_t beg)
436 {
437   char *tempfile = NULL;
438   mus_sample_t out_format = MUS_OUT_SAMPLE_TYPE;
439   io_error_t io_err = IO_NO_ERROR;
440 
441   if (mus_header_writable(MUS_NEXT, cp->sound->hdr->sample_type))
442     out_format = cp->sound->hdr->sample_type;
443   tempfile = snd_tempnam();
444 
445   io_err = save_selection(tempfile, snd_srate(cp->sound), out_format, MUS_NEXT, NULL, SAVE_ALL_CHANS);
446   if (io_err == IO_NO_ERROR)
447     {
448       int i;
449       sync_info *si_in;
450       si_in = selection_sync();
451       if (si_in)
452 	{
453 	  if (si_in->chans > 1)
454 	    remember_temp(tempfile, si_in->chans);
455 	  for (i = 0; ((i < si_in->chans) && (i < si_out->chans)); i++)
456 	    {
457 	      char *origin;
458 	      chan_info *cp_in, *cp_out;
459 	      mus_long_t len;
460 	      cp_out = si_out->cps[i]; /* currently syncd chan that we might paste to */
461 	      cp_in = si_in->cps[i];   /* selection chan to paste in (no wrap-around here) */
462 	      len = cp_selection_len(cp_in, NULL);
463 #if HAVE_FORTH
464 	      origin = mus_format("%" print_mus_long " %s", beg, S_insert_selection);
465 #else
466 	      origin = mus_format("%s" PROC_OPEN "%" print_mus_long, to_proc_name(S_insert_selection), beg);
467 #endif
468 	      if (file_insert_samples(beg, len,
469 				      tempfile, cp_out, i,
470 				      (si_in->chans > 1) ? MULTICHANNEL_DELETION : DELETE_ME,
471 				      origin, cp_out->edit_ctr))
472 		update_graph(cp_out);
473 	      free(origin);
474 	    }
475 	  free_sync_info(si_in);
476 	}
477     }
478   if (tempfile) free(tempfile);
479   return(io_err);
480 }
481 
482 
insert_selection_or_region(int reg,chan_info * cp)483 static void insert_selection_or_region(int reg, chan_info *cp)
484 {
485   if (cp)
486     {
487       io_error_t err = IO_NO_ERROR;
488       bool got_selection;
489       got_selection = ((reg == 0) && (selection_is_active()));
490       if (got_selection)
491 	{
492 	  sync_info *si_out;
493 	  si_out = sync_to_chan(cp);
494 	  err = insert_selection(cp, si_out, cursor_sample(cp));
495 	  free_sync_info(si_out);
496 	}
497       else err = paste_region(reg, cp);
498       if (err != IO_NO_ERROR)
499 	status_report(cp->sound, "can't insert %s: %s",
500 			     (got_selection) ? "selection" : "region",
501 			     io_error_name(err));
502     }
503 }
504 
505 
insert_selection_from_menu(void)506 void insert_selection_from_menu(void)
507 {
508   insert_selection_or_region(0, selected_channel());
509 }
510 
511 
512 /* we're drawing the selection in one channel, but others may be sync'd to it */
513 
start_selection_creation(chan_info * cp,mus_long_t samp)514 void start_selection_creation(chan_info *cp, mus_long_t samp)
515 {
516   int i;
517   if ((syncd_chans) &&
518       (selection_creates_region(ss)))
519     /* hmmm -- if keyboard selection in progress, then mouse press? */
520     make_region_from_selection();
521   deactivate_selection();
522   syncd_chans = sync_to_chan(cp);
523   syncd_chans->begs[0] = samp;           /* begs not otherwise used here, so treat as pivot point */
524   for (i = 0; i < syncd_chans->chans; i++)
525     reactivate_selection(syncd_chans->cps[i], samp, samp);
526 }
527 
528 
restart_selection_creation(chan_info * cp,bool right)529 void restart_selection_creation(chan_info *cp, bool right)
530 {
531   syncd_chans = sync_to_chan(cp);
532   if (right)
533     syncd_chans->begs[0] = selection_beg(cp);
534   else syncd_chans->begs[0] = selection_end(cp);
535 }
536 
537 
selection_creation_in_progress(void)538 bool selection_creation_in_progress(void) {return(syncd_chans != NULL);}
539 
540 
finish_selection_creation(void)541 void finish_selection_creation(void)
542 {
543   if (syncd_chans)
544     {
545       if (selection_creates_region(ss))
546 	make_region_from_selection();
547       enved_reflect_selection(true);
548       reflect_selection_in_save_as_dialog(true);
549 
550       if (Xen_hook_has_list(ss->effects_hook))
551 	run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
552 
553       syncd_chans = free_sync_info(syncd_chans);
554     }
555 }
556 
557 
558 static void show_selection_triangle(chan_info *cp, graphics_context *ax, int x0, int x1, mus_long_t beg, mus_long_t end);
559 
cp_redraw_selection(chan_info * cp)560 static void cp_redraw_selection(chan_info *cp)
561 {
562   int x0, x1;
563   mus_long_t beg, end;
564   axis_info *ap;
565   double sp_srate;
566   graphics_context *ax;
567 
568   ap = cp->axis;
569   beg = selection_beg(cp);
570   end = selection_end(cp);
571   sp_srate = (double)snd_srate(cp->sound);
572 
573   if (ap->losamp < beg)
574     x0 = grf_x((double)beg / sp_srate, ap);
575   else x0 = ap->x_axis_x0;
576 
577   if (ap->hisamp > end)
578     x1 = grf_x((double)end / sp_srate, ap);
579   else x1 = ap->x_axis_x1;
580 
581   ax = selection_context(cp);
582 
583   if (cp->selection_visible)
584     {
585       fill_rectangle(ax,
586 		     cp->old_x0,
587 		     ap->y_axis_y1,
588 		     cp->old_x1 - cp->old_x0,
589 		     (int)(ap->y_axis_y0 - ap->y_axis_y1));
590       show_selection_triangle(cp, ax, cp->old_x0, cp->old_x1, beg, end);
591     }
592 
593   fill_rectangle(ax,
594 		 x0,
595 		 ap->y_axis_y1,
596 		 x1 - x0,
597 		 (int)(ap->y_axis_y0 - ap->y_axis_y1));
598   show_selection_triangle(cp, ax, x0, x1, beg, end);
599 
600   cp->old_x0 = x0;
601   cp->old_x1 = x1;
602   cp->selection_visible = true;
603 }
604 
605 
redraw_selection(void)606 static void redraw_selection(void)
607 {
608   int i;
609   for (i = 0; i < ss->max_sounds; i++)
610     {
611       snd_info *sp;
612       sp = ss->sounds[i];
613       if (sp)
614 	{
615 	  if (sp->inuse == SOUND_NORMAL)
616 	    {
617 	      uint32_t j;
618 	      for (j = 0; j < sp->nchans; j++)
619 		{
620 		  chan_info *cp;
621 		  cp = sp->chans[j];
622 		  if ((cp) && (selection_is_visible(cp)))
623 		    {
624 		      cp_redraw_selection(cp);
625 		      if ((cp->graph_transform_on) &&
626 			  (!(chan_fft_in_progress(cp))) &&
627 			  (show_selection_transform(ss)))
628 			calculate_fft(cp);
629 		      if (sp->channel_style == CHANNELS_SUPERIMPOSED)
630 			break;
631 		    }
632 		}
633 	    }
634 	}
635     }
636 }
637 
638 
display_selection(chan_info * cp)639 void display_selection(chan_info *cp)
640 {
641   if (selection_is_visible(cp))
642     cp_redraw_selection(cp); /* draw just this chan */
643 }
644 
645 
update_possible_selection_in_progress(mus_long_t samp)646 void update_possible_selection_in_progress(mus_long_t samp)
647 {
648   if (syncd_chans)
649     {
650       int i;
651       mus_long_t original_beg;
652 
653       if (samp < 0) samp = 0;
654       original_beg = syncd_chans->begs[0];
655       for (i = 0; i < syncd_chans->chans; i++)
656 	{
657 	  chan_info *cp;
658 	  ed_list *ed;
659 	  mus_long_t new_end;
660 
661 	  cp = syncd_chans->cps[i];
662 	  ed = cp->edits[cp->edit_ctr];
663 	  ed->selection_maxamp = -1.0;
664 	  ed->selection_maxamp_position = -1;
665 	  if (samp >= current_samples(cp))
666 	    new_end = current_samples(cp) - 1;
667 	  else new_end = samp;
668 
669 	  if (new_end < original_beg)
670 	    {
671 	      ed->selection_beg = new_end;
672 	      ed->selection_end = original_beg;
673 	    }
674 	  else
675 	    {
676 	      ed->selection_beg = original_beg;
677 	      ed->selection_end = new_end;
678 	    }
679 	}
680       redraw_selection();
681     }
682 }
683 
684 
make_region_from_selection(void)685 int make_region_from_selection(void)
686 {
687   mus_long_t *ends = NULL;
688   int i, id = -1;
689   bool happy = false;
690   sync_info *si;
691 
692   if (!(selection_is_active())) return(-1);
693   if (max_regions(ss) == 0) return(-1);
694 
695   si = selection_sync();
696   ends = (mus_long_t *)calloc(si->chans, sizeof(mus_long_t));
697   for (i = 0; i < si->chans; i++)
698     {
699       ends[i] = selection_end(si->cps[i]);
700       if (ends[i] > si->begs[i]) happy = true;
701       /* C-space followed by mouse click causes a bogus selection-creation event */
702     }
703 
704   if (happy) id = define_region(si, ends);
705   free_sync_info(si);
706   if (ends) free(ends);
707   return(id);
708 }
709 
710 
select_all(chan_info * cp)711 int select_all(chan_info *cp)
712 {
713   if (cp)
714     {
715       sync_info *si;
716       int i;
717       deactivate_selection();
718       si = sync_to_chan(cp);
719       for (i = 0; i < si->chans; i++)
720 	{
721 	  if (current_samples(si->cps[i]) > 0)
722 	    {
723 	      reactivate_selection(si->cps[i], 0, current_samples(si->cps[i]));
724 	      update_graph(si->cps[i]);
725 	    }
726 	}
727       free_sync_info(si);
728       if ((selection_is_active()) && (selection_creates_region(ss)))
729 	return(make_region_from_selection());
730     }
731   return(-1);
732 }
733 
734 
735 /* ---------------- selection mouse motion ---------------- */
736 
737 static int last_selection_x = 0;
738 static timeout_result_t watch_selection_button = 0;
739 
cancel_selection_watch(void)740 void cancel_selection_watch(void)
741 {
742 #if (!USE_NO_GUI)
743   if (watch_selection_button)
744     TIMEOUT_REMOVE(watch_selection_button);
745 #endif
746   watch_selection_button = 0;
747 }
748 
749 static void move_selection_1(chan_info *cp, int x);
750 
751 #if (!USE_NO_GUI)
watch_selection(TIMEOUT_ARGS)752 static TIMEOUT_TYPE watch_selection(TIMEOUT_ARGS)
753 {
754   chan_info *cp = (chan_info *)context;
755   if (watch_selection_button)
756     {
757       move_selection_1(cp, last_selection_x);
758       watch_selection_button = CALL_TIMEOUT(watch_selection, 50, cp);
759     }
760   TIMEOUT_RESULT
761 }
762 #endif
763 
move_selection_1(chan_info * cp,int x)764 static void move_selection_1(chan_info *cp, int x)
765 {
766   axis_info *ap;
767   ap = cp->axis;
768   if ((x > ap->x_axis_x1) || (x < ap->x_axis_x0))
769     {
770       if (((x > ap->x_axis_x1) && (ap->x1 == ap->xmax)) ||
771 	  ((x < ap->x_axis_x0) && (ap->x0 == ap->xmin)))
772 	return;
773       move_axis(cp, x);
774       if (!watch_selection_button)
775 	watch_selection_button = CALL_TIMEOUT(watch_selection, 50, cp);
776     }
777   else
778     if (watch_selection_button)
779       cancel_selection_watch();
780   redraw_selection();
781 }
782 
783 
move_selection(chan_info * cp,int x)784 void move_selection(chan_info *cp, int x)
785 {
786   last_selection_x = x; /* called in snd-xchn -- sets last_selection_x */
787   move_selection_1(cp, x);
788 }
789 
790 
show_selection_triangle(chan_info * cp,graphics_context * ax,int x0,int x1,mus_long_t beg,mus_long_t end)791 static void show_selection_triangle(chan_info *cp, graphics_context *ax, int x0, int x1, mus_long_t beg, mus_long_t end)
792 {
793   int y0;
794 
795   y0 = ((axis_info *)(cp->axis))->y_axis_y0;
796   if ((cp->axis->losamp <= beg) &&
797       (cp->axis->hisamp > beg))
798     {
799       fill_polygon(ax, 4,
800 		   x0, y0,
801 		   x0 + play_arrow_size(ss), y0 + play_arrow_size(ss),
802 		   x0, y0 + 2 * play_arrow_size(ss),
803 		   x0, y0);
804     }
805 
806   if ((cp->axis->losamp < end) &&
807       (cp->axis->hisamp >= end))
808     {
809       fill_polygon(ax, 4,
810 		   x1, y0,
811 		   x1 - play_arrow_size(ss), y0 + play_arrow_size(ss),
812 		   x1, y0 + 2 * play_arrow_size(ss),
813 		   x1, y0);
814     }
815 }
816 
817 #define HIT_SLOP 4
818 
hit_selection_triangle(chan_info * cp,int x,int y)819 bool hit_selection_triangle(chan_info *cp, int x, int y)
820 {
821   axis_info *ap;
822   mus_long_t beg;
823   int mx;
824 
825   beg = selection_beg(cp);
826   ap = cp->axis;
827   if (beg < ap->losamp) return(false);
828   if (beg > ap->hisamp) return(false);
829 
830   mx = grf_x((double)beg / (double)snd_srate(cp->sound), ap);
831 
832   if (mx > (x + HIT_SLOP)) return(false);                                /* click point is to the left of the triangle */
833   if ((mx + play_arrow_size(ss) + HIT_SLOP) < x) return(false);  /* click point is to the right of the triangle */
834 
835   y = y - ap->y_axis_y0 - play_arrow_size(ss);
836   if (y < 0) y = -y;
837   return((mx + play_arrow_size(ss) - y + HIT_SLOP) >= x);
838 }
839 
840 
hit_selection_loop_triangle(chan_info * cp,int x,int y)841 bool hit_selection_loop_triangle(chan_info *cp, int x, int y)
842 {
843   axis_info *ap;
844   mus_long_t end;
845   int mx;
846 
847   end = selection_end(cp);
848   ap = cp->axis;
849   if (end < ap->losamp) return(false);
850   if (end > ap->hisamp) return(false);
851 
852   mx = grf_x((double)end / (double)snd_srate(cp->sound), ap);
853 
854   if ((mx - play_arrow_size(ss) - HIT_SLOP) > x) return(false);
855   if (mx < (x - HIT_SLOP)) return(false);
856 
857   y = y - ap->y_axis_y0 - play_arrow_size(ss);
858   if (y < 0) y = -y;
859 
860   return((mx - play_arrow_size(ss) - y - HIT_SLOP) <= x);
861 }
862 
863 
864 
865 
866 /* ---------------------------------------- selection object ---------------------------------------- */
867 
868 typedef struct {
869   int n;
870 } xen_selection;
871 
872 
873 #define Xen_to_xen_selection(arg) ((xen_selection *)Xen_object_ref(arg))
874 
875 static Xen_object_type_t xen_selection_tag;
876 
xen_is_selection(Xen obj)877 bool xen_is_selection(Xen obj)
878 {
879   return(Xen_c_object_is_type(obj, xen_selection_tag));
880 }
881 
882 #if (!HAVE_SCHEME)
xen_selection_free(xen_selection * v)883 static void xen_selection_free(xen_selection *v) {if (v) free(v);}
884 
Xen_wrap_free(xen_selection,free_xen_selection,xen_selection_free)885 Xen_wrap_free(xen_selection, free_xen_selection, xen_selection_free)
886 #else
887 static s7_pointer s7_xen_selection_free(s7_scheme *sc, s7_pointer obj)
888 {
889   xen_selection *v;
890   v = (xen_selection *)s7_c_object_value(obj);
891   if (v) free(v);
892   return(NULL);
893 }
894 #endif
895 
896 
897 static char *xen_selection_to_string(xen_selection *v)
898 {
899   #define xen_is_selectionRINT_BUFFER_SIZE 64
900   char *buf;
901   if (!v) return(NULL);
902   buf = (char *)calloc(xen_is_selectionRINT_BUFFER_SIZE, sizeof(char));
903   snprintf(buf, xen_is_selectionRINT_BUFFER_SIZE, "#<selection %d>", v->n);
904   return(buf);
905 }
906 
907 
908 #if HAVE_FORTH || HAVE_RUBY
Xen_wrap_print(xen_selection,print_xen_selection,xen_selection_to_string)909 Xen_wrap_print(xen_selection, print_xen_selection, xen_selection_to_string)
910 
911 static Xen g_xen_selection_to_string(Xen obj)
912 {
913   char *vstr;
914   Xen result;
915   #define S_xen_selection_to_string "selection->string"
916   Xen_check_type(xen_is_selection(obj), obj, 1, S_xen_selection_to_string, "a selection");
917   vstr = xen_selection_to_string(Xen_to_xen_selection(obj));
918   result = C_string_to_Xen_string(vstr);
919   free(vstr);
920   return(result);
921 }
922 #else
923 #if HAVE_SCHEME
g_xen_selection_to_string(s7_scheme * sc,s7_pointer args)924 static s7_pointer g_xen_selection_to_string(s7_scheme *sc, s7_pointer args)
925 {
926   char *vstr;
927   Xen result;
928   vstr = xen_selection_to_string(Xen_to_xen_selection(s7_car(args)));
929   result = C_string_to_Xen_string(vstr);
930   free(vstr);
931   return(result);
932 }
933 #endif
934 #endif
935 
936 
937 #if (!HAVE_SCHEME)
xen_selection_equalp(xen_selection * v1,xen_selection * v2)938 static bool xen_selection_equalp(xen_selection *v1, xen_selection *v2)
939 {
940   return((v1 == v2) ||
941 	 (v1->n == v2->n));
942 }
943 
944 
equalp_xen_selection(Xen obj1,Xen obj2)945 static Xen equalp_xen_selection(Xen obj1, Xen obj2)
946 {
947   if ((!(xen_is_selection(obj1))) || (!(xen_is_selection(obj2)))) return(Xen_false);
948   return(C_bool_to_Xen_boolean(xen_selection_equalp(Xen_to_xen_selection(obj1), Xen_to_xen_selection(obj2))));
949 }
950 #endif
951 
952 
xen_selection_make(int n)953 static xen_selection *xen_selection_make(int n)
954 {
955   xen_selection *new_v;
956   new_v = (xen_selection *)malloc(sizeof(xen_selection));
957   new_v->n = n;
958   return(new_v);
959 }
960 
961 
g_selection(void)962 static Xen g_selection(void)
963 {
964   #define H_selection "(" S_selection" ) returns an object representing the current selection, or " PROC_FALSE " if there is no active selection"
965   if (selection_is_active())
966     {
967       xen_selection *mx;
968       mx = xen_selection_make(xen_selection_counter);
969       return(Xen_make_object(xen_selection_tag, mx, 0, free_xen_selection));
970     }
971   return(Xen_false);
972 }
973 
974 
975 #if HAVE_SCHEME
s7_xen_selection_length(s7_scheme * sc,Xen args)976 static Xen s7_xen_selection_length(s7_scheme *sc, Xen args)
977 {
978   return(g_selection_framples(Xen_undefined, Xen_undefined));
979 }
980 
s7_xen_selection_is_equal(s7_scheme * sc,s7_pointer args)981 static s7_pointer s7_xen_selection_is_equal(s7_scheme *sc, s7_pointer args)
982 {
983   s7_pointer p1, p2;
984   p1 = s7_car(args);
985   p2 = s7_cadr(args);
986   if (p1 == p2) return(s7_t(sc));
987   if (s7_c_object_type(p2) == xen_selection_tag)
988     return(s7_make_boolean(sc, (((xen_selection *)s7_c_object_value(p1))->n == ((xen_selection *)s7_c_object_value(p2))->n)));
989   return(s7_f(sc));
990 }
991 
s7_xen_selection_copy(s7_scheme * sc,Xen args)992 static Xen s7_xen_selection_copy(s7_scheme *sc, Xen args)
993 {
994   if (selection_is_active())
995     {
996       snd_info *sp;
997       char *name;
998       name = snd_tempnam();
999       save_selection(name, selection_srate(), MUS_OUT_SAMPLE_TYPE, MUS_NEXT, NULL, SAVE_ALL_CHANS);
1000       sp = snd_open_file(name, FILE_READ_WRITE);
1001       free(name);
1002       return(new_xen_sound(sp->index));
1003     }
1004   return(Xen_false);
1005 }
1006 
1007 
s7_xen_selection_fill(s7_scheme * sc,Xen args)1008 static Xen s7_xen_selection_fill(s7_scheme *sc, Xen args)
1009 {
1010   sync_info *si;
1011   mus_float_t valf;
1012   s7_pointer val;
1013 
1014   val = s7_cadr(args);
1015   valf = Xen_real_to_C_double(val);
1016   if (valf == 0.0)
1017     {
1018       mus_float_t vals[1] = {0.0};
1019       scale_by(NULL, vals, 1, true); /* 1 entry in vals array, true = over selection */
1020       return(Xen_false);
1021     }
1022 
1023   si = selection_sync();
1024   if (si)
1025     {
1026       int i;
1027       for (i = 0; i < si->chans; i++)
1028 	{
1029 	  mus_long_t beg, end, len, j;
1030 	  mus_float_t *data;
1031 	  beg = selection_beg(si->cps[i]);
1032 	  end = selection_end(si->cps[i]);
1033 	  len = end - beg + 1;
1034 	  data = (mus_float_t *)malloc(len * sizeof(mus_float_t));
1035 	  for (j = 0; j < len; j++)
1036 	    data[j] = valf;
1037 	  if (change_samples(beg, len, data, si->cps[i], "fill! selection", si->cps[i]->edit_ctr, fabs(valf)))
1038 	    update_graph(si->cps[i]);
1039 	  free(data);
1040 	}
1041     }
1042   return(Xen_false);
1043 }
1044 #endif
1045 
1046 
init_xen_selection(void)1047 static void init_xen_selection(void)
1048 {
1049 #if HAVE_SCHEME
1050   xen_selection_tag = s7_make_c_type(s7, "<selection>");
1051   s7_c_type_set_gc_free(s7, xen_selection_tag, s7_xen_selection_free);
1052   s7_c_type_set_is_equal(s7, xen_selection_tag, s7_xen_selection_is_equal);
1053   s7_c_type_set_length(s7, xen_selection_tag, s7_xen_selection_length);
1054   s7_c_type_set_copy(s7, xen_selection_tag, s7_xen_selection_copy);
1055   s7_c_type_set_fill(s7, xen_selection_tag, s7_xen_selection_fill);
1056   s7_c_type_set_to_string(s7, xen_selection_tag, g_xen_selection_to_string);
1057 #else
1058 #if HAVE_RUBY
1059   xen_selection_tag = Xen_make_object_type("XenSelection", sizeof(xen_selection));
1060 #else
1061   xen_selection_tag = Xen_make_object_type("selection", sizeof(xen_selection));
1062 #endif
1063 #endif
1064 
1065 #if HAVE_FORTH
1066   fth_set_object_inspect(xen_selection_tag,   print_xen_selection);
1067   fth_set_object_dump(xen_selection_tag,      g_xen_selection_to_string);
1068   fth_set_object_equal(xen_selection_tag,     equalp_xen_selection);
1069   fth_set_object_free(xen_selection_tag,      free_xen_selection);
1070 #endif
1071 
1072 #if HAVE_RUBY
1073   rb_define_method(xen_selection_tag, "to_s",     Xen_procedure_cast print_xen_selection, 0);
1074   rb_define_method(xen_selection_tag, "eql?",     Xen_procedure_cast equalp_xen_selection, 1);
1075   rb_define_method(xen_selection_tag, "==",       Xen_procedure_cast equalp_xen_selection, 1);
1076   rb_define_method(xen_selection_tag, "to_str",   Xen_procedure_cast g_xen_selection_to_string, 0);
1077 #endif
1078 }
1079 /* -------------------------------------------------------------------------------- */
1080 
1081 
1082 
save_selection(const char * ofile,int srate,mus_sample_t samp_type,mus_header_t head_type,const char * comment,int chan)1083 io_error_t save_selection(const char *ofile, int srate, mus_sample_t samp_type, mus_header_t head_type, const char *comment, int chan)
1084 {
1085   /* type and format have already been checked */
1086   int ofd;
1087   io_error_t io_err = IO_NO_ERROR;
1088   mus_long_t oloc, alloc_len;
1089   sync_info *si;
1090   mus_long_t *ends;
1091   int i, j, k, chans;
1092   mus_long_t dur;
1093   snd_fd **sfs;
1094   snd_info *sp = NULL;
1095   mus_float_t **data;
1096 
1097   si = selection_sync();
1098   if ((si) && (si->cps) && (si->cps[0])) sp = si->cps[0]->sound;
1099 
1100   if (head_type == MUS_UNKNOWN_HEADER)
1101     {
1102       if ((sp) && (mus_header_writable(sp->hdr->type, MUS_IGNORE_SAMPLE)))
1103 	head_type = sp->hdr->type;
1104       else head_type = MUS_NEXT;
1105     }
1106   if (samp_type == MUS_UNKNOWN_SAMPLE)
1107     {
1108       if ((sp) && (mus_header_writable(head_type, sp->hdr->sample_type)))
1109 	samp_type = sp->hdr->sample_type;
1110       else samp_type = MUS_OUT_SAMPLE_TYPE;
1111     }
1112   if (!mus_header_writable(head_type, samp_type))
1113     {
1114       head_type = MUS_NEXT;
1115       samp_type = MUS_OUT_SAMPLE_TYPE;
1116     }
1117   if (srate == -1)
1118     srate = selection_srate();
1119 
1120   dur = selection_len();
1121   if (chan == SAVE_ALL_CHANS)
1122     chans = si->chans;
1123   else chans = 1;
1124 
1125   io_err = snd_write_header(ofile, head_type, srate, chans, chans * dur, samp_type, comment, NULL);
1126   if (io_err != IO_NO_ERROR)
1127     {
1128       free_sync_info(si);
1129       return(io_err);
1130     }
1131 
1132   oloc = mus_header_data_location();
1133   ofd = snd_reopen_write(ofile);
1134 
1135   if (sp)
1136     {
1137       int bps;
1138       mus_long_t num;
1139       disk_space_t no_space;
1140       bool copy_ok = false;
1141 
1142       bps = mus_bytes_per_sample(samp_type);
1143       num = dur * bps * chans;
1144 
1145       no_space = disk_has_space(num, ofile);
1146       if (no_space != DISK_SPACE_OK)
1147 	{
1148 	  snd_close(ofd, ofile);
1149 	  free_sync_info(si);
1150 	  return(IO_DISK_FULL);
1151 	}
1152 
1153       copy_ok = ((samp_type == sp->hdr->sample_type) &&
1154 		 (chans == (int)sp->nchans) &&
1155 		 (chan == SAVE_ALL_CHANS));
1156       if (copy_ok)
1157 	for (i = 0; i < chans; i++)
1158 	  if ((sp->chans[i]->edit_ctr != 0) ||
1159 	      (si->cps[i]->sound != sp) ||
1160 	      (si->begs[i] != si->begs[0]))
1161 	    {
1162 	      copy_ok = false;
1163 	      break;
1164 	    }
1165       if (copy_ok)
1166 	{
1167 	  /* write next header with correct len
1168 	   * seek loc in sp->filename
1169 	   * copy len*data-size bytes
1170 	   * get max from amp envs
1171 	   */
1172 	  int fdi;
1173 	  lseek(ofd, oloc, SEEK_SET);
1174 	  fdi = mus_file_open_read(sp->filename); /* this does not read the header */
1175 	  if (fdi == -1)
1176 	    {
1177 	      snd_close(ofd, ofile);
1178 	      free_sync_info(si);
1179 	      return(IO_CANT_READ_SELECTION_FILE);
1180 	    }
1181 	  /* snd_error("can't read selection's original sound? %s: %s", sp->filename, snd_io_strerror()); */
1182 	  else
1183 	    {
1184 	      mus_long_t iloc;
1185 	      char *buffer;
1186 
1187 	      iloc = mus_sound_data_location(sp->filename);
1188 	      lseek(fdi, iloc + chans * bps * si->begs[0], SEEK_SET);
1189 	      buffer = (char *)malloc(MAX_BUFFER_SIZE * sizeof(char));
1190 	      for (j = 0; j < num; j += MAX_BUFFER_SIZE)
1191 		{
1192 		  ssize_t n;
1193 		  mus_long_t bytes;
1194 		  bytes = num - j;
1195 		  if (bytes > MAX_BUFFER_SIZE) bytes = MAX_BUFFER_SIZE;
1196 		  n = read(fdi, buffer, bytes);
1197 		  if (n != 0)
1198 		    n = write(ofd, buffer, bytes);
1199 		  if (n == 0)
1200 		    fprintf(stderr, "IO error while saving selection");
1201 		}
1202 	      free(buffer);
1203 	      snd_close(fdi, sp->filename);
1204 	    }
1205 	  snd_close(ofd, ofile);
1206 	  free_sync_info(si);
1207 #if USE_MOTIF
1208 	  if (!(ss->file_monitor_ok))
1209 	    alert_new_file();
1210 #endif
1211 	  return(IO_NO_ERROR);
1212 	}
1213     }
1214 
1215   ends = (mus_long_t *)calloc(chans, sizeof(mus_long_t));
1216   sfs = (snd_fd **)calloc(chans, sizeof(snd_fd *));
1217   if (chan == SAVE_ALL_CHANS)
1218     {
1219       for (i = 0; i < chans; i++)
1220 	{
1221 	  ends[i] = selection_end(si->cps[i]);
1222 	  sfs[i] = init_sample_read(selection_beg(si->cps[i]), si->cps[i], READ_FORWARD);
1223 	}
1224     }
1225   else
1226     {
1227       ends[0] = selection_end(si->cps[chan]);
1228       sfs[0] = init_sample_read(selection_beg(si->cps[chan]), si->cps[chan], READ_FORWARD);
1229     }
1230 
1231   snd_file_open_descriptors(ofd, ofile, samp_type, oloc, chans, head_type);
1232   mus_file_set_clipping(ofd, clipping(ss));
1233   lseek(ofd, oloc, SEEK_SET);
1234   data = (mus_float_t **)calloc(chans, sizeof(mus_float_t *));
1235 
1236   if (dur > REPORTING_SIZE)
1237     alloc_len = REPORTING_SIZE;
1238   else alloc_len = dur;
1239 
1240   for (i = 0; i < chans; i++)
1241     data[i] = (mus_float_t *)calloc(alloc_len, sizeof(mus_float_t));
1242 
1243   if (alloc_len == dur)
1244     {
1245       for (k = 0; k < chans; k++)
1246 	samples_to_vct_with_reader(dur, data[k], sfs[k]);
1247       mus_file_write(ofd, 0, dur - 1, chans, data);
1248     }
1249   else
1250     {
1251       mus_long_t ioff;
1252       ss->stopped_explicitly = false;
1253       for (k = 0; k < chans; k++)
1254 	sampler_set_safe(sfs[k], ends[k]);
1255 
1256       for (ioff = 0; ioff < dur; ioff += alloc_len)
1257 	{
1258 	  mus_long_t kdur;
1259 
1260 	  kdur = dur - ioff;
1261 	  if (kdur > alloc_len) kdur = alloc_len;
1262 
1263 	  for (j = 0; j < kdur; j++)
1264 	    {
1265 	      for (k = 0; k < chans; k++)
1266 		{
1267 		  if ((ioff + j) <= ends[k])
1268 		    data[k][j] = read_sample(sfs[k]);
1269 		  else data[k][j] = 0.0;
1270 		}
1271 	    }
1272 	  io_err = sndlib_error_to_snd(mus_file_write(ofd, 0, j - 1, chans, data));
1273 	  if (io_err != IO_NO_ERROR)
1274 	    {
1275 	      snd_warning("%s %s: %s",
1276 			  io_error_name(io_err),
1277 			  ofile,
1278 			  snd_io_strerror());
1279 	      break;
1280 	    }
1281 	  if (ss->stopped_explicitly)
1282 	    {
1283 	      ss->stopped_explicitly = false;
1284 	      snd_warning_without_format("save selection stopped");
1285 	      io_err = IO_INTERRUPTED;
1286 	      break;
1287 	    }
1288 	}
1289     }
1290 
1291   for (i = 0; i < chans; i++)
1292     {
1293       free_snd_fd(sfs[i]);
1294       free(data[i]);
1295     }
1296   free(sfs);
1297   free(data);
1298   free_sync_info(si);
1299   free(ends);
1300 
1301   if (mus_file_close(ofd) != 0)
1302     return(IO_CANT_CLOSE_FILE);
1303 #if USE_MOTIF
1304   if (!(ss->file_monitor_ok))
1305     alert_new_file();
1306 #endif
1307 
1308   return(io_err);
1309 }
1310 
1311 
g_delete_selection(void)1312 static Xen g_delete_selection(void)
1313 {
1314   #define H_delete_selection "(" S_delete_selection "): delete the currently selected portion"
1315   if (selection_is_active())
1316     {
1317       delete_selection(UPDATE_DISPLAY);
1318       return(Xen_true);
1319     }
1320   return(snd_no_active_selection_error(S_delete_selection));
1321 }
1322 
1323 
g_insert_selection(Xen beg,Xen snd,Xen chn)1324 static Xen g_insert_selection(Xen beg, Xen snd, Xen chn)
1325 {
1326   #define H_insert_selection "(" S_insert_selection " :optional (beg 0) snd chn): insert the currently selected portion starting at beg"
1327   if (selection_is_active())
1328     {
1329       chan_info *cp;
1330       mus_long_t samp;
1331       io_error_t io_err = IO_NO_ERROR;
1332       sync_info *si_out;
1333 
1334       Snd_assert_channel(S_insert_selection, snd, chn, 2);
1335       Xen_check_type(Xen_is_integer_or_unbound(beg), beg, 1, S_insert_selection, "an integer");
1336 
1337       cp = get_cp(snd, chn, S_insert_selection);
1338       if ((!cp) || (!(is_editable(cp)))) return(Xen_false);
1339 
1340       samp = beg_to_sample(beg, S_insert_selection);
1341       if (Xen_is_integer(chn))
1342 	si_out = make_simple_sync(cp, samp); /* ignore sync */
1343       else si_out = sync_to_chan(cp);
1344 
1345       io_err = insert_selection(cp, si_out, samp);
1346       free_sync_info(si_out);
1347 
1348       if (is_serious_io_error(io_err))
1349 	Xen_error(Xen_make_error_type("IO-error"),
1350 		  Xen_list_2(C_string_to_Xen_string(S_insert_selection ": IO error ~A"),
1351 			     C_string_to_Xen_string(io_error_name(io_err))));
1352       return(Xen_false);
1353     }
1354   return(snd_no_active_selection_error(S_insert_selection));
1355 }
1356 
1357 
g_mix_selection(Xen beg,Xen snd,Xen chn,Xen sel_chan)1358 static Xen g_mix_selection(Xen beg, Xen snd, Xen chn, Xen sel_chan)
1359 {
1360   #define H_mix_selection "(" S_mix_selection " :optional (beg 0) snd chn (selection-channel " PROC_TRUE ")): mix the currently selected portion starting at beg"
1361   if (selection_is_active())
1362     {
1363       chan_info *cp;
1364       mus_long_t obeg;
1365       io_error_t io_err = IO_NO_ERROR;
1366       int i, selection_chan = 0, id = -1, chans = 0;
1367       sync_info *si_out;
1368       Xen result = Xen_empty_list;
1369 
1370       Snd_assert_channel(S_mix_selection, snd, chn, 2);
1371       Xen_check_type(Xen_is_integer_or_unbound(beg), beg, 1, S_mix_selection, "an integer");
1372       Xen_check_type(Xen_is_integer_boolean_or_unbound(sel_chan), sel_chan, 4, S_mix_selection, "an integer or " PROC_TRUE);
1373 
1374       cp = get_cp(snd, chn, S_mix_selection);
1375       if ((!cp) || (!(is_editable(cp)))) return(Xen_false);
1376 
1377       obeg = beg_to_sample(beg, S_mix_selection);
1378       if (Xen_is_integer(sel_chan))
1379 	selection_chan = Xen_integer_to_C_int(sel_chan);
1380       if (Xen_is_integer(chn))
1381 	si_out = make_simple_sync(cp, obeg); /* ignore sync */
1382       else si_out = sync_to_chan(cp);
1383 
1384       id = mix_selection(cp, si_out, obeg, &io_err, selection_chan);
1385       chans = si_out->chans;                 /* save for loop below */
1386       free_sync_info(si_out);
1387 
1388       if (is_serious_io_error(io_err))
1389 	Xen_error(Xen_make_error_type("IO-error"),
1390 		  Xen_list_2(C_string_to_Xen_string(S_mix_selection ": IO error ~A"),
1391 			     C_string_to_Xen_string(io_error_name(io_err))));
1392 
1393       if (id == -1) return(Xen_false);
1394       for (i = 0; i < chans; i++)
1395 	result = Xen_cons(new_xen_mix(id + i), result);
1396       return(Xen_list_reverse(result));
1397     }
1398   return(snd_no_active_selection_error(S_mix_selection));
1399 }
1400 
1401 
g_selection_to_mix(void)1402 static Xen g_selection_to_mix(void)
1403 {
1404   #define H_selection_to_mix "(" S_selection_to_mix "): turns the current selection into a mix"
1405   if (selection_is_active())
1406     {
1407       chan_info *cp;
1408       io_error_t io_err;
1409       int i, id = INVALID_MIX_ID, chans = 0, sync = GET_NEW_SYNC;
1410       sync_info *si_out;
1411       Xen result = Xen_empty_list;
1412       char *tempfile, *origin = NULL;
1413 
1414       si_out = selection_sync();
1415       cp = si_out->cps[0];
1416 
1417       tempfile = snd_tempnam();
1418       io_err = save_selection(tempfile, snd_srate(cp->sound), MUS_OUT_SAMPLE_TYPE, MUS_NEXT, NULL, SAVE_ALL_CHANS);
1419       if (is_serious_io_error(io_err))
1420 	{
1421 	  if (tempfile) free(tempfile);
1422 	  free_sync_info(si_out);
1423 	  Xen_error(Xen_make_error_type("IO-error"),
1424 		    Xen_list_2(C_string_to_Xen_string(S_selection_to_mix ": IO error ~A"),
1425 			       C_string_to_Xen_string(io_error_name(io_err))));
1426 	}
1427 
1428       origin = mus_format("%s", S_selection_to_mix);
1429       if (si_out->chans > 1)
1430 	remember_temp(tempfile, si_out->chans);
1431 
1432       g_scale_selection_by(C_double_to_Xen_real(0.0));
1433 
1434       id = mix_file(selection_beg(NULL), selection_len(), si_out->chans, si_out->cps, tempfile,
1435 		    (si_out->chans > 1) ? MULTICHANNEL_DELETION : DELETE_ME,
1436 		    origin, true,
1437 		    0);
1438 
1439       deactivate_selection();
1440       free(origin);
1441       if (tempfile) free(tempfile);
1442       chans = si_out->chans;                 /* save for loop below */
1443       free_sync_info(si_out);
1444 
1445       if (id == -1) return(Xen_false);
1446       if (chans == 1)
1447 	return(Xen_cons(new_xen_mix(id), Xen_empty_list)); /* no sync */
1448 
1449       for (i = 0; i < chans; i++)
1450 	{
1451 	  sync = mix_set_sync_from_id(id + i, sync);
1452 	  result = Xen_cons(new_xen_mix(id + i), result);
1453 	}
1454 
1455       if ((mix_dialog_mix() >= id) &&
1456 	  (mix_dialog_mix() < (id + chans)))
1457 	reflect_mix_change(id);
1458        /* this update is needed in a case like: file close (closing old mix), open new, mix -- this mix can now have old mix's id */
1459       return(Xen_list_reverse(result));
1460     }
1461   return(snd_no_active_selection_error(S_selection_to_mix));
1462 }
1463 
1464 
g_is_selection(Xen sel)1465 static Xen g_is_selection(Xen sel)
1466 {
1467   #define H_is_selection "(" S_is_selection " :optional obj): " PROC_TRUE " if selection is currently active, visible, etc. \
1468 If 'obj' is passed, " S_is_selection " returns " PROC_TRUE " if obj is a selection object and there is a current selection."
1469 
1470   if ((Xen_is_bound(sel)) &&
1471       (!(xen_is_selection(sel))))
1472     return(Xen_false);
1473 
1474   return(C_bool_to_Xen_boolean(selection_is_active()));
1475 }
1476 
1477 
g_selection_position(Xen snd,Xen chn)1478 static Xen g_selection_position(Xen snd, Xen chn)
1479 {
1480   #define H_selection_position "(" S_selection_position " :optional snd chn): selection start samp"
1481   if (selection_is_active())
1482     {
1483       if (!Xen_is_bound(snd))
1484 	return(C_llong_to_Xen_llong(selection_beg(NULL)));
1485       else
1486 	{
1487 	  chan_info *cp;
1488 	  Snd_assert_channel(S_selection_position, snd, chn, 1);
1489 	  cp = get_cp(snd, chn, S_selection_position);
1490 	  if (!cp) return(Xen_false);
1491 	  return(C_llong_to_Xen_llong(selection_beg(cp)));
1492 	}
1493     }
1494   return(snd_no_active_selection_error(S_selection_position));
1495 }
1496 
1497 
g_set_selection_position(Xen pos,Xen snd,Xen chn)1498 static Xen g_set_selection_position(Xen pos, Xen snd, Xen chn)
1499 {
1500   chan_info *cp;
1501   mus_long_t beg;
1502 
1503   Snd_assert_channel(S_set S_selection_position, snd, chn, 2);
1504   Xen_check_type(Xen_is_integer(pos), pos, 1, S_selection_position, "an integer");
1505 
1506   beg = beg_to_sample(pos, S_set S_selection_position);
1507   if (!Xen_is_bound(snd))
1508     {
1509       sync_info *si = NULL;
1510       if (selection_is_active())
1511 	si = selection_sync();
1512       else
1513 	{
1514 	  cp = current_channel();
1515 	  if (cp) si = sync_to_chan(cp);
1516 	}
1517       if (si)
1518 	{
1519 	  int i;
1520 	  for (i = 0; i < si->chans; i++)
1521 	    cp_set_selection_beg(si->cps[i], beg);
1522 	  free_sync_info(si);
1523 	}
1524     }
1525   else
1526     {
1527       cp = get_cp(snd, chn, S_set S_selection_position);
1528       if (!cp) return(Xen_false);
1529       cp_set_selection_beg(cp, beg);
1530     }
1531   redraw_selection();
1532   return(pos);
1533 }
1534 
with_three_setter_args(g_set_selection_position_reversed,g_set_selection_position)1535 with_three_setter_args(g_set_selection_position_reversed, g_set_selection_position)
1536 
1537 
1538 Xen g_selection_framples(Xen snd, Xen chn)
1539 {
1540   #define H_selection_framples "(" S_selection_framples " :optional snd chn): selection length"
1541   if (selection_is_active())
1542     {
1543       if (!Xen_is_bound(snd))
1544 	return(C_llong_to_Xen_llong(selection_len()));
1545       else
1546 	{
1547 	  chan_info *cp;
1548 	  Snd_assert_channel(S_selection_framples, snd, chn, 1);
1549 	  cp = get_cp(snd, chn, S_selection_framples);
1550 	  if (!cp) return(Xen_false);
1551 	  return(C_llong_to_Xen_llong(cp_selection_len(cp, NULL)));
1552 	}
1553     }
1554   return(snd_no_active_selection_error(S_selection_framples));
1555 }
1556 
1557 
g_set_selection_framples(Xen samps,Xen snd,Xen chn)1558 static Xen g_set_selection_framples(Xen samps, Xen snd, Xen chn)
1559 {
1560   chan_info *cp;
1561   mus_long_t len;
1562 
1563   Xen_check_type(Xen_is_llong(samps), samps, 1, S_set S_selection_framples, "an integer");
1564   len = Xen_llong_to_C_llong(samps);
1565   if (len <= 0)
1566     Xen_wrong_type_arg_error(S_set S_selection_framples, 1, samps, "a positive integer");
1567   if (!Xen_is_bound(snd))
1568     {
1569       sync_info *si = NULL;
1570       if (selection_is_active())
1571 	si = selection_sync();
1572       else
1573 	{
1574 	  cp = current_channel();
1575 	  if (cp) si = sync_to_chan(cp);
1576 	}
1577       if (si)
1578 	{
1579 	  int i;
1580 	  for (i = 0; i < si->chans; i++)
1581 	    cp_set_selection_len(si->cps[i], len);
1582 	  free_sync_info(si);
1583 	}
1584     }
1585   else
1586     {
1587       Snd_assert_channel(S_set S_selection_framples, snd, chn, 2);
1588       cp = get_cp(snd, chn, S_set S_selection_framples);
1589       if (!cp) return(Xen_false);
1590       cp_set_selection_len(cp, len);
1591     }
1592   redraw_selection();
1593   return(samps);
1594 }
1595 
with_three_setter_args(g_set_selection_framples_reversed,g_set_selection_framples)1596 with_three_setter_args(g_set_selection_framples_reversed, g_set_selection_framples)
1597 
1598 
1599 static Xen g_selection_member(Xen snd, Xen chn)
1600 {
1601   #define H_selection_member "(" S_selection_member " :optional snd chn): " PROC_TRUE " if snd's channel chn is a member of the current selection"
1602   chan_info *cp;
1603   Snd_assert_channel(S_selection_member, snd, chn, 1);
1604   cp = get_cp(snd, chn, S_selection_member);
1605   if (!cp) return(Xen_false);
1606   return(C_bool_to_Xen_boolean(selection_is_active_in_channel(cp)));
1607 }
1608 
1609 
g_set_selection_member(Xen on,Xen snd,Xen chn)1610 static Xen g_set_selection_member(Xen on, Xen snd, Xen chn)
1611 {
1612   Xen_check_type(Xen_is_boolean(on), on, 1, S_set S_selection_member, "a boolean");
1613   if ((Xen_is_true(snd)) && (Xen_is_false(on)))
1614     deactivate_selection();
1615   else
1616     {
1617       chan_info *cp;
1618       Snd_assert_channel(S_set S_selection_member, snd, chn, 2);
1619       cp = get_cp(snd, chn, S_set S_selection_member);
1620       if (!cp) return(Xen_false);
1621       if (Xen_is_true(on))
1622 	{
1623 	  if (selection_is_active())
1624 	    cp_set_selection_beg(cp, selection_beg(NULL));
1625 	  else cp_set_selection_beg(cp, 0);
1626 	}
1627       else cp_deactivate_selection(cp);
1628       enved_reflect_selection(selection_is_active());
1629       reflect_selection_in_save_as_dialog(selection_is_active());
1630 
1631       if (Xen_hook_has_list(ss->effects_hook))
1632 	run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
1633 
1634       if (selection_is_active())
1635 	redraw_selection();
1636     }
1637   return(on);
1638 }
1639 
with_three_setter_args(g_set_selection_member_reversed,g_set_selection_member)1640 with_three_setter_args(g_set_selection_member_reversed, g_set_selection_member)
1641 
1642 
1643 static Xen g_select_all(Xen snd_n, Xen chn_n)
1644 {
1645   #define H_select_all "(" S_select_all " :optional snd chn): make a new selection containing all of snd's channel chn. \
1646 If sync is set, all chans are included.  The new region id is returned (if " S_selection_creates_region " is " PROC_TRUE ")."
1647   chan_info *cp;
1648   int id;
1649 
1650   Snd_assert_channel(S_select_all, snd_n, chn_n, 1);
1651   cp = get_cp(snd_n, chn_n, S_select_all);
1652   if (!cp) return(Xen_false);
1653 
1654   id = select_all(cp);
1655   if (selection_creates_region(ss))
1656     return(C_int_to_Xen_region(id));
1657   else return(Xen_false);
1658 }
1659 
1660 
1661 #define H_save_selection "(" S_save_selection " file srate sample-type header-type comment channel): \
1662 save the current selection in file using the indicated file attributes.  If channel is given, save only that channel."
1663 
1664 #if HAVE_SCHEME
g_save_selection(s7_scheme * sc,s7_pointer args)1665 static s7_pointer g_save_selection(s7_scheme *sc, s7_pointer args)
1666 {
1667   mus_header_t head_type;
1668   mus_sample_t samp_type;
1669   int sr, chn;
1670   io_error_t io_err;
1671   const char *com, *file;
1672   char *fname;
1673   s7_pointer p, fp, res;
1674 
1675   if (!(selection_is_active()))
1676     return(snd_no_active_selection_error(S_save_selection));
1677 
1678   fp = s7_car(args);
1679   if (fp == Xen_false)
1680     Xen_error(Xen_make_error_type("IO-error"), Xen_list_1(C_string_to_Xen_string(S_save_selection ": no output file?")));
1681   if (!s7_is_string(fp))
1682     return(s7_wrong_type_arg_error(sc, S_save_selection, 1, fp, "a string"));
1683   file = s7_string(fp);
1684   res = fp;
1685 
1686   fp = s7_cadr(args);
1687   if (fp == Xen_false)
1688     sr = selection_srate();
1689   else
1690     {
1691       if (!s7_is_integer(fp))
1692 	return(s7_wrong_type_arg_error(sc, S_save_selection, 2, fp, "an integer"));
1693       sr = s7_integer(fp);
1694       if (sr <= 0)
1695 	Xen_error(Xen_make_error_type("cannot-save"),
1696 		  Xen_list_2(C_string_to_Xen_string(S_save_selection ": srate (~A) can't be <= 0"), fp));
1697     }
1698 
1699   p = s7_cddr(args);
1700   fp = s7_car(p);
1701   if (fp == Xen_false)
1702     samp_type = MUS_UNKNOWN_SAMPLE;
1703   else
1704     {
1705       if (!s7_is_integer(fp))
1706 	return(s7_wrong_type_arg_error(sc, S_save_selection, 3, fp, "an integer"));
1707       samp_type = (mus_sample_t)s7_integer(fp);
1708     }
1709 
1710   fp = s7_cadr(p);
1711   if (fp == Xen_false)
1712     head_type = MUS_UNKNOWN_HEADER;
1713   else
1714     {
1715       if (!s7_is_integer(fp))
1716 	return(s7_wrong_type_arg_error(sc, S_save_selection, 4, fp, "an integer"));
1717       head_type = (mus_header_t)s7_integer(fp);
1718     }
1719   if ((head_type != MUS_UNKNOWN_HEADER) && (!(mus_header_writable(head_type, MUS_IGNORE_SAMPLE))))
1720     Xen_error(Xen_make_error_type("cannot-save"),
1721 	      Xen_list_2(C_string_to_Xen_string(S_save_selection ": can't write a ~A header"),
1722 			 C_string_to_Xen_string(mus_header_type_name(head_type))));
1723 
1724   if ((head_type != MUS_UNKNOWN_HEADER) && (samp_type != MUS_UNKNOWN_SAMPLE) && (!(mus_header_writable(head_type, samp_type))))
1725     Xen_error(Xen_make_error_type("cannot-save"),
1726 	      Xen_list_3(C_string_to_Xen_string(S_save_selection ": can't write ~A data to a ~A header"),
1727 			 C_string_to_Xen_string(mus_sample_type_name(samp_type)),
1728 			 C_string_to_Xen_string(mus_header_type_name(head_type))));
1729 
1730   p = s7_cddr(p);
1731   fp = s7_car(p);
1732   if (fp == Xen_false)
1733     com = NULL;
1734   else
1735     {
1736       if (!s7_is_string(fp))
1737 	return(s7_wrong_type_arg_error(sc, S_save_selection, 5, fp, "a string"));
1738       com = s7_string(fp);
1739     }
1740 
1741   fp = s7_cadr(p);
1742   if (fp == Xen_false)
1743     chn = SAVE_ALL_CHANS;
1744   else
1745     {
1746       if (!s7_is_integer(fp))
1747 	return(s7_wrong_type_arg_error(sc, S_save_selection, 6, fp, "an integer"));
1748       chn = s7_integer(fp);
1749     }
1750 
1751   fname = mus_expand_filename(file);
1752   io_err = save_selection(fname, sr, samp_type, head_type, com, chn);
1753 
1754   if (fname) free(fname);
1755   if ((io_err != IO_NO_ERROR) &&
1756       (io_err != IO_INTERRUPTED) &&
1757       (io_err != IO_SAVE_HOOK_CANCELLATION))
1758     Xen_error(Xen_make_error_type("cannot-save"),
1759 	      Xen_list_3(C_string_to_Xen_string(S_save_selection ": can't save ~S, ~A"), res, C_string_to_Xen_string(snd_open_strerror())));
1760   return(res);
1761 }
1762 #else
1763 static Xen kw_header_type, kw_comment, kw_file, kw_srate, kw_channel, kw_sample_type;
1764 
init_selection_keywords(void)1765 static void init_selection_keywords(void)
1766 {
1767   kw_header_type = Xen_make_keyword("header-type");
1768   kw_sample_type = Xen_make_keyword("sample-type");
1769   kw_comment = Xen_make_keyword("comment");
1770   kw_file = Xen_make_keyword("file");
1771   kw_srate = Xen_make_keyword("srate");
1772   kw_channel = Xen_make_keyword("channel");
1773 }
1774 
g_save_selection(Xen arglist)1775 static Xen g_save_selection(Xen arglist)
1776 {
1777   mus_header_t head_type = MUS_UNKNOWN_HEADER;
1778   mus_sample_t samp_type = MUS_UNKNOWN_SAMPLE;
1779   int sr = -1, chn = 0;
1780   io_error_t io_err = IO_NO_ERROR;
1781   const char *com = NULL, *file = NULL;
1782   char *fname = NULL;
1783   Xen args[12];
1784   Xen keys[6];
1785   int orig_arg[6] = {0, 0, 0, 0, 0, 0};
1786   int vals, i, arglist_len;
1787   if (!(selection_is_active()))
1788     return(snd_no_active_selection_error(S_save_selection));
1789 
1790   /* changed 7-Dec-08 to be more like save-sound-as in default values -- if just one
1791    *   sound involved, or all sounds same, use current choices rather than MUS_NEXT etc:
1792    *   hdr->type|srate|format
1793    */
1794 
1795   keys[0] = kw_file;
1796   keys[1] = kw_srate;
1797   keys[2] = kw_sample_type;
1798   keys[3] = kw_header_type;
1799   keys[4] = kw_comment;
1800   keys[5] = kw_channel;
1801 
1802   for (i = 0; i < 12; i++) args[i] = Xen_undefined;
1803   arglist_len = Xen_list_length(arglist);
1804   if (arglist_len > 12)
1805     Xen_out_of_range_error(S_save_selection, 0, arglist, "too many arguments");
1806   for (i = 0; i < arglist_len; i++) args[i] = Xen_list_ref(arglist, i);
1807 
1808   vals = mus_optkey_unscramble(S_save_selection, arglist_len, 6, keys, args, orig_arg);
1809   if (vals > 0)
1810     {
1811       file = mus_optkey_to_string(keys[0], S_save_selection, orig_arg[0], NULL);
1812       sr = mus_optkey_to_int(keys[1], S_save_selection, orig_arg[1], selection_srate());
1813       samp_type = (mus_sample_t)mus_optkey_to_int(keys[2], S_save_selection, orig_arg[2], (int)samp_type);
1814       head_type = (mus_header_t)mus_optkey_to_int(keys[3], S_save_selection, orig_arg[3], (int)head_type);
1815       com = mus_optkey_to_string(keys[4], S_save_selection, orig_arg[4], NULL);
1816       chn = mus_optkey_to_int(keys[5], S_save_selection, orig_arg[5], SAVE_ALL_CHANS);
1817     }
1818 
1819   if (!file)
1820     Xen_error(Xen_make_error_type("IO-error"),
1821 	      Xen_list_1(C_string_to_Xen_string(S_save_selection ": no output file?")));
1822 
1823   if ((head_type != MUS_UNKNOWN_HEADER) && (!(mus_header_writable(head_type, MUS_IGNORE_SAMPLE))))
1824     Xen_error(Xen_make_error_type("cannot-save"),
1825 	      Xen_list_2(C_string_to_Xen_string(S_save_selection ": can't write a ~A header"),
1826 			 C_string_to_Xen_string(mus_header_type_name(head_type))));
1827 
1828   if ((head_type != MUS_UNKNOWN_HEADER) && (samp_type != MUS_UNKNOWN_SAMPLE) && (!(mus_header_writable(head_type, samp_type))))
1829     Xen_error(Xen_make_error_type("cannot-save"),
1830 	      Xen_list_3(C_string_to_Xen_string(S_save_selection ": can't write ~A data to a ~A header"),
1831 			 C_string_to_Xen_string(mus_sample_type_name(samp_type)),
1832 			 C_string_to_Xen_string(mus_header_type_name(head_type))));
1833 
1834   if ((sr != -1) && (sr <= 0))
1835     Xen_error(Xen_make_error_type("cannot-save"),
1836 	      Xen_list_2(C_string_to_Xen_string(S_save_selection ": srate (~A) can't be <= 0"),
1837 			 C_int_to_Xen_integer(sr)));
1838 
1839   fname = mus_expand_filename(file);
1840   io_err = save_selection(fname, sr, samp_type, head_type, com, chn);
1841 
1842   if (fname) free(fname);
1843   if ((io_err != IO_NO_ERROR) &&
1844       (io_err != IO_INTERRUPTED) &&
1845       (io_err != IO_SAVE_HOOK_CANCELLATION))
1846     Xen_error(Xen_make_error_type("cannot-save"),
1847 	      Xen_list_3(C_string_to_Xen_string(S_save_selection ": can't save ~S, ~A"),
1848 			 keys[0],
1849 			 C_string_to_Xen_string(snd_open_strerror())));
1850   return(args[orig_arg[0] - 1]);
1851 }
1852 #endif
1853 
g_selection_chans(void)1854 Xen g_selection_chans(void)
1855 {
1856   #define H_selection_chans "(" S_selection_chans "): chans in active selection"
1857   return(C_int_to_Xen_integer(selection_chans()));
1858 }
1859 
1860 
g_selection_srate(void)1861 Xen g_selection_srate(void)
1862 {
1863   #define H_selection_srate "(" S_selection_srate "): selection srate"
1864   return(C_int_to_Xen_integer(selection_srate()));
1865 }
1866 
1867 
g_selection_maxamp(Xen snd,Xen chn)1868 Xen g_selection_maxamp(Xen snd, Xen chn)
1869 {
1870   #define H_selection_maxamp "(" S_selection_maxamp " :optional snd chn): selection maxamp in given channel, or overall maxamp if no args passed."
1871   if (Xen_is_bound(snd))
1872     {
1873       chan_info *cp;
1874       Snd_assert_channel(S_selection_maxamp, snd, chn, 1);
1875       cp = get_cp(snd, chn, S_selection_maxamp);
1876       if (!cp) return(Xen_false);
1877       return(C_double_to_Xen_real(selection_maxamp(cp)));
1878     }
1879   else
1880     {
1881       mus_float_t mx = 0.0;
1882       int i;
1883       sync_info *si;
1884       si = selection_sync();
1885       if (!si)
1886 	return(C_double_to_Xen_real(0.0)); /* no selection -- error? */
1887       for (i = 0; i < si->chans; i++)
1888 	{
1889 	  mus_float_t cur_mx;
1890 	  cur_mx = selection_maxamp(si->cps[i]);
1891 	  if (cur_mx > mx)
1892 	    mx = cur_mx;
1893 	}
1894       free_sync_info(si);
1895       return(C_double_to_Xen_real(mx));
1896     }
1897 }
1898 
1899 
g_selection_maxamp_position(Xen snd,Xen chn)1900 static Xen g_selection_maxamp_position(Xen snd, Xen chn)
1901 {
1902   #define H_selection_maxamp_position "(" S_selection_maxamp_position " :optional snd chn): location of selection maxamp (0 = start of selection)"
1903   chan_info *cp;
1904   Snd_assert_channel(S_selection_maxamp_position, snd, chn, 1);
1905   cp = get_cp(snd, chn, S_selection_maxamp_position);
1906   if (!cp) return(Xen_false);
1907   return(C_llong_to_Xen_llong(selection_maxamp_position(cp)));
1908 }
1909 
1910 
1911 static double sel_beg, sel_end;
1912 
get_selection_bounds(chan_info * cp)1913 static bool get_selection_bounds(chan_info *cp)
1914 {
1915   if (selection_is_active_in_channel(cp))
1916     {
1917       mus_long_t samp;
1918       double x;
1919       samp = selection_beg(cp);
1920       x = (double)samp / snd_srate(cp->sound);
1921       if ((sel_beg < 0.0) || (x < sel_beg))
1922 	sel_beg = x;
1923       samp = selection_end(cp);
1924       x = (double)samp / snd_srate(cp->sound);
1925       if ((sel_end < 0.0) || (x > sel_end))
1926 	sel_end = x;
1927     }
1928   return(false);
1929 }
1930 
update_bounds(chan_info * cp)1931 static bool update_bounds(chan_info *cp)
1932 {
1933   set_x_axis_x0x1(cp, sel_beg, sel_end);
1934   return(false);
1935 }
1936 
1937 
show_selection(void)1938 void show_selection(void)
1939 {
1940   sel_beg = -1.0;
1941   sel_end = -1.0;
1942   map_over_chans(get_selection_bounds);
1943   map_over_chans(update_bounds);
1944 }
1945 
1946 
g_show_selection(void)1947 static Xen g_show_selection(void)
1948 {
1949   #define H_show_selection "(" S_show_selection ") adjusts graph bounds to display the current selection in full"
1950   if (selection_is_active())
1951     show_selection();
1952   return(Xen_false);
1953 }
1954 
1955 
g_unselect_all(void)1956 static Xen g_unselect_all(void)
1957 {
1958   #define H_unselect_all "(" S_unselect_all ") deactivates (unselects) the current selection."
1959   deactivate_selection();
1960   return(Xen_false);
1961 }
1962 
1963 
Xen_wrap_2_optional_args(g_selection_position_w,g_selection_position)1964 Xen_wrap_2_optional_args(g_selection_position_w, g_selection_position)
1965 Xen_wrap_2_optional_args(g_selection_framples_w, g_selection_framples)
1966 Xen_wrap_2_optional_args(g_selection_member_w, g_selection_member)
1967 Xen_wrap_no_args(g_selection_w, g_selection)
1968 Xen_wrap_1_optional_arg(g_is_selection_w, g_is_selection)
1969 Xen_wrap_no_args(g_selection_chans_w, g_selection_chans)
1970 Xen_wrap_no_args(g_selection_srate_w, g_selection_srate)
1971 Xen_wrap_2_optional_args(g_selection_maxamp_w, g_selection_maxamp)
1972 Xen_wrap_2_optional_args(g_selection_maxamp_position_w, g_selection_maxamp_position)
1973 Xen_wrap_no_args(g_delete_selection_w, g_delete_selection)
1974 Xen_wrap_3_optional_args(g_insert_selection_w, g_insert_selection)
1975 Xen_wrap_4_optional_args(g_mix_selection_w, g_mix_selection)
1976 Xen_wrap_no_args(g_selection_to_mix_w, g_selection_to_mix)
1977 Xen_wrap_2_optional_args(g_select_all_w, g_select_all)
1978 Xen_wrap_no_args(g_show_selection_w, g_show_selection)
1979 Xen_wrap_no_args(g_unselect_all_w, g_unselect_all)
1980 #if HAVE_SCHEME
1981 #define g_set_selection_position_w g_set_selection_position_reversed
1982 #define g_set_selection_framples_w g_set_selection_framples_reversed
1983 #define g_set_selection_member_w g_set_selection_member_reversed
1984 #else
1985 Xen_wrap_any_args(g_save_selection_w, g_save_selection)
1986 Xen_wrap_3_optional_args(g_set_selection_position_w, g_set_selection_position)
1987 Xen_wrap_3_optional_args(g_set_selection_framples_w, g_set_selection_framples)
1988 Xen_wrap_3_optional_args(g_set_selection_member_w, g_set_selection_member)
1989 #endif
1990 
1991 void g_init_selection(void)
1992 {
1993 #if HAVE_SCHEME
1994   s7_pointer i, b, t, f, sel, p;
1995   i = s7_make_symbol(s7, "integer?");
1996   sel = s7_make_symbol(s7, "selection?");
1997   b = s7_make_symbol(s7, "boolean?");
1998   f = s7_make_symbol(s7, "float?");
1999   p = s7_make_symbol(s7, "pair?");
2000   t = s7_t(s7);
2001 #endif
2002 
2003 #if (!HAVE_SCHEME)
2004   init_selection_keywords();
2005 #endif
2006   init_xen_selection();
2007 
2008   Xen_define_typed_dilambda(S_selection_position, g_selection_position_w, H_selection_position, S_set S_selection_position, g_set_selection_position_w, 0, 2, 1, 2,
2009 			    s7_make_signature(s7, 3, i, t, t), s7_make_signature(s7, 4, i, t, t, i));
2010   Xen_define_typed_dilambda(S_selection_framples, g_selection_framples_w, H_selection_framples, S_set S_selection_framples, g_set_selection_framples_w, 0, 2, 1, 2,
2011 			    s7_make_signature(s7, 3, i, t, t), s7_make_signature(s7, 4, i, t, t, i));
2012   Xen_define_typed_dilambda(S_selection_member, g_selection_member_w, H_selection_member, S_set S_selection_member, g_set_selection_member_w, 0, 2, 1, 2,
2013 			    s7_make_signature(s7, 3, b, t, t), s7_make_signature(s7, 4, b, t, t, b));
2014 
2015   Xen_define_typed_procedure(S_selection,        g_selection_w,        0, 0, 0, H_selection,         s7_make_signature(s7, 1, s7_make_signature(s7, 2, sel, b)));
2016   Xen_define_typed_procedure(S_is_selection,     g_is_selection_w,     0, 1, 0, H_is_selection,      s7_make_signature(s7, 2, b, t));
2017   Xen_define_typed_procedure(S_selection_chans,  g_selection_chans_w,  0, 0, 0, H_selection_chans,   s7_make_signature(s7, 1, i));
2018   Xen_define_typed_procedure(S_selection_srate,  g_selection_srate_w,  0, 0, 0, H_selection_srate,   s7_make_signature(s7, 1, i));
2019   Xen_define_typed_procedure(S_selection_maxamp, g_selection_maxamp_w, 0, 2, 0, H_selection_maxamp,  s7_make_signature(s7, 3, f, t, t));
2020   Xen_define_typed_procedure(S_selection_maxamp_position, g_selection_maxamp_position_w, 0, 2, 0, H_selection_maxamp_position, s7_make_signature(s7, 3, i, t, t));
2021   Xen_define_typed_procedure(S_select_all,       g_select_all_w,       0, 2, 0, H_select_all,        s7_make_signature(s7, 3, b, t, t));
2022   Xen_define_typed_procedure(S_unselect_all,     g_unselect_all_w,     0, 0, 0, H_unselect_all,      s7_make_signature(s7, 1, b));
2023   Xen_define_typed_procedure(S_delete_selection, g_delete_selection_w, 0, 0, 0, H_delete_selection,  s7_make_signature(s7, 1, b));
2024   Xen_define_typed_procedure(S_insert_selection, g_insert_selection_w, 0, 3, 0, H_insert_selection,  s7_make_signature(s7, 4, b, i, t, t));
2025   Xen_define_typed_procedure(S_mix_selection,    g_mix_selection_w,    0, 4, 0, H_mix_selection,     s7_make_signature(s7, 5, t, i, t, t, i));
2026   Xen_define_typed_procedure(S_selection_to_mix, g_selection_to_mix_w, 0, 0, 0, H_selection_to_mix,  s7_make_signature(s7, 1, p));
2027   Xen_define_typed_procedure(S_show_selection,   g_show_selection_w,   0, 0, 0, H_show_selection,    s7_make_signature(s7, 1, b));
2028 #if HAVE_SCHEME
2029   s7_define_safe_function_star(s7, S_save_selection, g_save_selection, "file srate sample-type header-type comment channel", H_save_selection);
2030 #else
2031   Xen_define_typed_procedure(S_save_selection,   g_save_selection_w,   0, 0, 1, H_save_selection,    s7_make_circular_signature(s7, 0, 1, t));
2032 #endif
2033 }
2034