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