summaryrefslogtreecommitdiff
path: root/snd-select.c
diff options
context:
space:
mode:
authorAlessio Treglia <quadrispro@ubuntu.com>2009-10-19 09:55:11 +0200
committerAlessio Treglia <quadrispro@ubuntu.com>2009-10-19 09:55:11 +0200
commit5cd66eecc95be11cacc5aaf4db8c67a499bb2d4d (patch)
treef9fe35437c9a69b886676bbdeff692ebc728bec2 /snd-select.c
Imported Upstream version 11
Diffstat (limited to 'snd-select.c')
-rw-r--r--snd-select.c1474
1 files changed, 1474 insertions, 0 deletions
diff --git a/snd-select.c b/snd-select.c
new file mode 100644
index 0000000..c156314
--- /dev/null
+++ b/snd-select.c
@@ -0,0 +1,1474 @@
+#include "snd.h"
+#include "clm2xen.h"
+
+
+/* -------- watcher lists -------- */
+
+typedef struct {
+ void (*watcher)(selection_watcher_reason_t reason, void *data);
+ void *context;
+} selection_watcher;
+
+static selection_watcher **selection_watchers = NULL;
+static int selection_watchers_size = 0;
+
+#define SELECTION_WATCHER_SIZE_INCREMENT 2
+
+
+int add_selection_watcher(void (*watcher)(selection_watcher_reason_t reason, void *data), void *context)
+{
+ int loc = -1;
+ if (!(selection_watchers))
+ {
+ loc = 0;
+ selection_watchers_size = SELECTION_WATCHER_SIZE_INCREMENT;
+ selection_watchers = (selection_watcher **)calloc(selection_watchers_size, sizeof(selection_watcher *));
+ }
+ else
+ {
+ int i;
+ for (i = 0; i < selection_watchers_size; i++)
+ if (!(selection_watchers[i]))
+ {
+ loc = i;
+ break;
+ }
+ if (loc == -1)
+ {
+ loc = selection_watchers_size;
+ selection_watchers_size += SELECTION_WATCHER_SIZE_INCREMENT;
+ selection_watchers = (selection_watcher **)realloc(selection_watchers, selection_watchers_size * sizeof(selection_watcher *));
+ for (i = loc; i < selection_watchers_size; i++) selection_watchers[i] = NULL;
+ }
+ }
+ selection_watchers[loc] = (selection_watcher *)calloc(1, sizeof(selection_watcher));
+ selection_watchers[loc]->watcher = watcher;
+ selection_watchers[loc]->context = context;
+ return(loc);
+}
+
+
+void remove_selection_watcher(int loc)
+{
+ if ((selection_watchers) &&
+ (loc < selection_watchers_size) &&
+ (loc >= 0) &&
+ (selection_watchers[loc]))
+ {
+ free(selection_watchers[loc]);
+ selection_watchers[loc] = NULL;
+ }
+}
+
+
+void call_selection_watchers(selection_watcher_reason_t reason)
+{
+ if (selection_watchers)
+ {
+ int i;
+ for (i = 0; i < selection_watchers_size; i++)
+ if (selection_watchers[i])
+ (*(selection_watchers[i]->watcher))(reason, selection_watchers[i]->context);
+ }
+ run_watchers();
+}
+
+
+
+static bool cp_has_selection(chan_info *cp)
+{
+ ed_list *ed;
+ ed = cp->edits[cp->edit_ctr];
+ return((ed) && (ed->selection_beg != NO_SELECTION));
+}
+
+
+static bool map_over_chans(bool (*func)(chan_info *ncp))
+{
+ /* non-zero = abort map, skips inactive sounds */
+ int i, j;
+ bool val = false;
+ for (i = 0; i < ss->max_sounds; i++)
+ {
+ snd_info *sp;
+ chan_info *cp;
+ sp = ss->sounds[i];
+ if ((sp) && (sp->inuse == SOUND_NORMAL))
+ for (j = 0; j < sp->nchans; j++)
+ if ((cp = ((chan_info *)(sp->chans[j]))))
+ if (cp)
+ {
+ val = (*func)(cp);
+ if (val) return(val);
+ }
+ }
+ return(val);
+}
+
+
+bool selection_is_active(void)
+{
+ /* is selection active in any channel */
+ return(map_over_chans(cp_has_selection));
+}
+
+
+bool selection_is_active_in_channel(chan_info *cp)
+{
+ return((cp) && (cp_has_selection(cp)));
+}
+
+
+static bool selection_is_visible(chan_info *cp)
+{
+ ed_list *ed;
+ axis_info *ap;
+
+ ed = cp->edits[cp->edit_ctr];
+ if (ed->selection_beg == NO_SELECTION) return(false);
+
+ ap = cp->axis;
+ return((ed) &&
+ (ap->losamp < ed->selection_end) &&
+ (ap->hisamp > ed->selection_beg));
+}
+
+
+bool selection_is_visible_in_channel(chan_info *cp)
+{
+ return((cp_has_selection(cp)) &&
+ (selection_is_visible(cp)));
+}
+
+
+static mus_long_t mus_long_t_map_over_chans(mus_long_t (*func)(chan_info *, mus_long_t *), mus_long_t *userptr)
+{
+ int i, j;
+ mus_long_t val = 0;
+
+ for (i = 0; i < ss->max_sounds; i++)
+ {
+ snd_info *sp;
+ chan_info *cp;
+
+ sp = ss->sounds[i];
+ if ((sp) && (sp->inuse == SOUND_NORMAL))
+ for (j = 0; j < sp->nchans; j++)
+ if ((cp = ((chan_info *)(sp->chans[j]))))
+ {
+ val = (*func)(cp, userptr);
+ if (val) return(val);
+ }
+ }
+ return(val);
+}
+
+
+static mus_long_t cp_selection_beg(chan_info *cp, mus_long_t *beg)
+{
+ ed_list *ed;
+
+ ed = cp->edits[cp->edit_ctr];
+ if (ed->selection_beg != NO_SELECTION)
+ {
+ beg[0] = ed->selection_beg;
+ return(1); /* i.e. stop map_over_chans */
+ }
+
+ return(0);
+}
+
+
+mus_long_t selection_beg(chan_info *cp)
+{
+ mus_long_t beg[1];
+ beg[0] = 0;
+ if (cp)
+ cp_selection_beg(cp, beg);
+ else mus_long_t_map_over_chans(cp_selection_beg, beg);
+ return(beg[0]);
+}
+
+
+static void cp_set_selection_beg(chan_info *cp, mus_long_t beg)
+{
+ ed_list *ed;
+ mus_long_t len;
+
+ ed = cp->edits[cp->edit_ctr];
+ len = CURRENT_SAMPLES(cp);
+ if (beg < len)
+ ed->selection_beg = beg;
+ else ed->selection_beg = len - 1;
+
+ ed->selection_maxamp = -1.0;
+ ed->selection_maxamp_position = -1;
+}
+
+
+mus_long_t selection_end(chan_info *cp) /* never called without selection_member check in advance */
+{
+ return(cp->edits[cp->edit_ctr]->selection_end);
+}
+
+
+static mus_long_t cp_selection_len(chan_info *cp, mus_long_t *ptr)
+{
+ ed_list *ed;
+ ed = cp->edits[cp->edit_ctr];
+ if ((ed) && (ed->selection_beg != NO_SELECTION))
+ return(ed->selection_end - ed->selection_beg + 1);
+ return(0);
+}
+
+
+mus_long_t selection_len(void)
+{
+ return(mus_long_t_map_over_chans(cp_selection_len, NULL));
+}
+
+
+static void cp_set_selection_len(chan_info *cp, mus_long_t len)
+{
+ ed_list *ed;
+ mus_long_t cplen;
+ ed = cp->edits[cp->edit_ctr];
+ cplen = CURRENT_SAMPLES(cp);
+ ed->selection_end = ed->selection_beg + len - 1;
+ if (ed->selection_end >= cplen) ed->selection_end = cplen - 1;
+ ed->selection_maxamp = -1.0;
+ ed->selection_maxamp_position = -1;
+}
+
+
+static void selection_chans_1(chan_info *cp, int *counter)
+{
+ if (cp_has_selection(cp)) counter[0]++;
+}
+
+
+int selection_chans(void)
+{
+ int count[1];
+ count[0] = 0;
+ for_each_normal_chan_with_refint(selection_chans_1, count);
+ return(count[0]);
+}
+
+
+static mus_long_t selection_srate_1(chan_info *cp, mus_long_t *ignored)
+{
+ if (cp_has_selection(cp))
+ return((mus_long_t)SND_SRATE(cp->sound));
+ return(0);
+}
+
+
+int selection_srate(void)
+{
+ if (selection_is_active())
+ return((int)mus_long_t_map_over_chans(selection_srate_1, NULL));
+ return(0);
+}
+
+
+mus_float_t selection_maxamp(chan_info *cp)
+{
+ mus_float_t val = 0.0;
+ if (selection_is_active_in_channel(cp))
+ {
+ mus_long_t maxpos = 0;
+ val = ed_selection_maxamp(cp);
+ if (val >= 0.0) return(val);
+ val = channel_local_maxamp(cp,
+ selection_beg(cp),
+ selection_end(cp) - selection_beg(cp) + 1,
+ cp->edit_ctr,
+ &maxpos);
+ set_ed_selection_maxamp(cp, val);
+ set_ed_selection_maxamp_position(cp, maxpos);
+ }
+ return(val);
+}
+
+
+static mus_long_t selection_maxamp_position(chan_info *cp)
+{
+ selection_maxamp(cp);
+ return(ed_selection_maxamp_position(cp));
+}
+
+
+static void cp_delete_selection(chan_info *cp)
+{
+ ed_list *ed;
+ ed = cp->edits[cp->edit_ctr];
+ if ((ed) && (ed->selection_beg != NO_SELECTION))
+ {
+ delete_samples(ed->selection_beg, cp_selection_len(cp, NULL), cp, cp->edit_ctr);
+ ed = cp->edits[cp->edit_ctr];
+ ed->selection_beg = NO_SELECTION;
+ }
+}
+
+
+bool delete_selection(cut_selection_regraph_t regraph)
+{
+ if (selection_is_active())
+ {
+ for_each_normal_chan(cp_delete_selection);
+ if (regraph == UPDATE_DISPLAY) for_each_normal_chan(update_graph);
+ call_selection_watchers(SELECTION_INACTIVE);
+ return(true);
+ }
+ return(false);
+}
+
+
+static void cp_deactivate_selection(chan_info *cp)
+{
+ ed_list *ed;
+ ed = cp->edits[cp->edit_ctr];
+ if (ed) ed->selection_beg = NO_SELECTION;
+}
+
+
+static sync_info *syncd_chans = NULL;
+
+void deactivate_selection(void)
+{
+ for_each_normal_chan(cp_deactivate_selection);
+ for_each_normal_chan(update_graph);
+ call_selection_watchers(SELECTION_INACTIVE);
+ if (syncd_chans)
+ syncd_chans = free_sync_info(syncd_chans);
+}
+
+
+void reactivate_selection(chan_info *cp, mus_long_t beg, mus_long_t end)
+{
+ ed_list *ed;
+ mus_long_t len;
+
+ ed = cp->edits[cp->edit_ctr];
+ len = CURRENT_SAMPLES(cp) - 1;
+ if (beg < 0) beg = 0;
+ if (end < 0) end = 0;
+ if (beg > len) beg = len;
+ if (end > len) end = len;
+ if (beg > end) end = beg;
+
+ ed->selection_beg = beg;
+ ed->selection_end = end;
+ cp->selection_visible = false;
+ ed->selection_maxamp = -1.0;
+ ed->selection_maxamp_position = -1;
+
+ call_selection_watchers(SELECTION_ACTIVE);
+}
+
+
+void ripple_selection(ed_list *ed, mus_long_t beg, mus_long_t num)
+{
+ /* beg = insert or delete begin point (snd-edits.c), num = samps inserted (num positive) or deleted (num negative) at beg */
+ if (ed->selection_beg != NO_SELECTION)
+ {
+ if (beg < ed->selection_beg)
+ {
+ ed->selection_beg += num;
+ if (beg >= ed->selection_beg)
+ ed->selection_beg = NO_SELECTION; /* deletion included some of current selection from outside */
+ else ed->selection_end += num;
+ }
+ else
+ {
+ if (beg < ed->selection_end)
+ {
+ ed->selection_end += num;
+ if (ed->selection_end < beg)
+ ed->selection_beg = NO_SELECTION; /* same as above but from end */
+ }
+ }
+ }
+}
+
+
+static void next_selection_chan(chan_info *cp, void *sidata)
+{
+ if (cp_has_selection(cp))
+ {
+ sync_info *si = (sync_info *)sidata;
+ int chan;
+ chan = si->chans;
+ si->chans++;
+ si->begs[chan] = selection_beg(cp);
+ si->cps[chan] = cp;
+ }
+}
+
+
+sync_info *selection_sync(void)
+{
+ sync_info *si;
+ if (!(selection_is_active())) return(NULL);
+ si = (sync_info *)calloc(1, sizeof(sync_info));
+ si->chans = selection_chans();
+ si->cps = (chan_info **)calloc(si->chans, sizeof(chan_info *));
+ si->begs = (mus_long_t *)calloc(si->chans, sizeof(mus_long_t));
+ si->chans = 0;
+ for_each_normal_chan_with_void(next_selection_chan, (void *)si);
+ return(si);
+}
+
+
+static int mix_selection(chan_info *cp, sync_info *si_out, mus_long_t beg, io_error_t *err, int start_chan)
+{
+ char *tempfile = NULL, *origin = NULL;
+ int id = INVALID_MIX_ID;
+ io_error_t io_err = IO_NO_ERROR;
+
+ tempfile = snd_tempnam();
+ io_err = save_selection(tempfile, MUS_NEXT, MUS_OUT_FORMAT, SND_SRATE(cp->sound), NULL, SAVE_ALL_CHANS);
+ if (io_err == IO_NO_ERROR)
+ {
+#if HAVE_FORTH
+ origin = mus_format(MUS_LD " snd chn %s", beg, S_mix_selection);
+#else
+ origin = mus_format("%s" PROC_OPEN MUS_LD, TO_PROC_NAME(S_mix_selection), beg);
+#endif
+ if (si_out->chans > 1)
+ remember_temp(tempfile, si_out->chans);
+
+ id = mix_file(beg, selection_len(), si_out->chans, si_out->cps, tempfile,
+ (si_out->chans > 1) ? MULTICHANNEL_DELETION : DELETE_ME,
+ origin, with_mix_tags(ss),
+ start_chan);
+ free(origin);
+ }
+ if (tempfile) free(tempfile);
+ (*err) = io_err;
+
+ return(id);
+}
+
+
+void add_selection_or_region(int reg, chan_info *cp)
+{
+ /* in all cases, this has a local sound to report in (kbd, xmenu) */
+ if (cp)
+ {
+ if (editable_p(cp))
+ {
+ io_error_t io_err = IO_NO_ERROR;
+ bool got_selection;
+ got_selection = ((reg == 0) && (selection_is_active()));
+ if (got_selection)
+ {
+ sync_info *si_out;
+ si_out = sync_to_chan(cp);
+ mix_selection(cp, si_out, CURSOR(cp), &io_err, 0);
+ free_sync_info(si_out);
+ }
+ else io_err = add_region(reg, cp);
+ if (io_err != IO_NO_ERROR)
+ report_in_minibuffer(cp->sound, "can't mix %s: %s",
+ (got_selection) ? "selection" : "region",
+ io_error_name(io_err));
+ }
+ }
+ else snd_error_without_format("no channel to mix into?");
+}
+
+
+static io_error_t insert_selection(chan_info *cp, sync_info *si_out, mus_long_t beg)
+{
+ char *tempfile = NULL, *origin = NULL;
+ int i, out_format = MUS_OUT_FORMAT;
+ io_error_t io_err = IO_NO_ERROR;
+
+ if (mus_header_writable(MUS_NEXT, cp->sound->hdr->format))
+ out_format = cp->sound->hdr->format;
+ tempfile = snd_tempnam();
+
+ io_err = save_selection(tempfile, MUS_NEXT, out_format, SND_SRATE(cp->sound), NULL, SAVE_ALL_CHANS);
+ if (io_err == IO_NO_ERROR)
+ {
+ sync_info *si_in;
+ si_in = selection_sync();
+ if (si_in)
+ {
+ if (si_in->chans > 1)
+ remember_temp(tempfile, si_in->chans);
+ for (i = 0; ((i < si_in->chans) && (i < si_out->chans)); i++)
+ {
+ chan_info *cp_in, *cp_out;
+ mus_long_t len;
+ cp_out = si_out->cps[i]; /* currently syncd chan that we might paste to */
+ cp_in = si_in->cps[i]; /* selection chan to paste in (no wrap-around here) */
+ len = cp_selection_len(cp_in, NULL);
+#if HAVE_FORTH
+ origin = mus_format(MUS_LD " %s", beg, S_insert_selection);
+#else
+ origin = mus_format("%s" PROC_OPEN MUS_LD, TO_PROC_NAME(S_insert_selection), beg);
+#endif
+ if (file_insert_samples(beg, len,
+ tempfile, cp_out, i,
+ (si_in->chans > 1) ? MULTICHANNEL_DELETION : DELETE_ME,
+ origin, cp_out->edit_ctr))
+ update_graph(cp_out);
+ free(origin);
+ }
+ free_sync_info(si_in);
+ }
+ }
+ if (tempfile) free(tempfile);
+ return(io_err);
+}
+
+
+void insert_selection_or_region(int reg, chan_info *cp)
+{
+ io_error_t err = IO_NO_ERROR;
+ if (cp)
+ {
+ bool got_selection;
+ got_selection = ((reg == 0) && (selection_is_active()));
+ if (got_selection)
+ {
+ sync_info *si_out;
+ si_out = sync_to_chan(cp);
+ err = insert_selection(cp, si_out, CURSOR(cp));
+ free_sync_info(si_out);
+ }
+ else err = paste_region(reg, cp);
+ if (err != IO_NO_ERROR)
+ report_in_minibuffer(cp->sound, "can't insert %s: %s",
+ (got_selection) ? "selection" : "region",
+ io_error_name(err));
+ }
+}
+
+
+void insert_selection_from_menu(void)
+{
+ insert_selection_or_region(0, selected_channel());
+}
+
+
+/* we're drawing the selection in one channel, but others may be sync'd to it */
+
+void start_selection_creation(chan_info *cp, mus_long_t samp)
+{
+ int i;
+ if ((syncd_chans) &&
+ (selection_creates_region(ss)))
+ /* hmmm -- if keyboard selection in progress, then mouse press? */
+ make_region_from_selection();
+ deactivate_selection();
+ syncd_chans = sync_to_chan(cp);
+ syncd_chans->begs[0] = samp; /* begs not otherwise used here, so treat as pivot point */
+ for (i = 0; i < syncd_chans->chans; i++)
+ reactivate_selection(syncd_chans->cps[i], samp, samp);
+}
+
+
+bool selection_creation_in_progress(void) {return(syncd_chans != NULL);}
+
+
+void finish_selection_creation(void)
+{
+ if (syncd_chans)
+ {
+ if (selection_creates_region(ss))
+ make_region_from_selection();
+ call_selection_watchers(SELECTION_ACTIVE);
+ syncd_chans = free_sync_info(syncd_chans);
+ }
+}
+
+
+static void cp_redraw_selection(chan_info *cp)
+{
+ int x0, x1;
+ mus_long_t beg, end;
+ axis_info *ap;
+ double sp_srate;
+ ap = cp->axis;
+ beg = selection_beg(cp);
+ end = selection_end(cp);
+ sp_srate = (double)SND_SRATE(cp->sound);
+ if (ap->losamp < beg)
+ x0 = grf_x((double)beg / sp_srate, ap);
+ else x0 = ap->x_axis_x0;
+ if (ap->hisamp > end)
+ x1 = grf_x((double)end / sp_srate, ap);
+ else x1 = ap->x_axis_x1;
+#if USE_CAIRO
+ if (cp->selection_visible)
+ {
+ axis_context *ax;
+ ax = selection_context(cp);
+ cairo_set_source_rgb(ax->cr, ax->gc->bg_color->red, ax->gc->bg_color->green, ax->gc->bg_color->blue);
+ cairo_rectangle(ax->cr, cp->old_x0, ap->y_axis_y1, cp->old_x1 - cp->old_x0, (int)(ap->y_axis_y0 - ap->y_axis_y1));
+ cairo_fill(ax->cr);
+ }
+#else
+ if (cp->selection_visible)
+ fill_rectangle(selection_context(cp),
+ cp->old_x0,
+ ap->y_axis_y1,
+ cp->old_x1 - cp->old_x0,
+ (int)(ap->y_axis_y0 - ap->y_axis_y1));
+#endif
+ fill_rectangle(selection_context(cp),
+ x0,
+ ap->y_axis_y1,
+ x1 - x0,
+ (int)(ap->y_axis_y0 - ap->y_axis_y1));
+ cp->old_x0 = x0;
+ cp->old_x1 = x1;
+ cp->selection_visible = true;
+#if USE_CAIRO
+ make_graph(cp);
+#endif
+}
+
+
+static void redraw_selection(void)
+{
+ int i;
+ for (i = 0; i < ss->max_sounds; i++)
+ {
+ snd_info *sp;
+ sp = ss->sounds[i];
+ if (sp)
+ {
+ if (sp->inuse == SOUND_NORMAL)
+ {
+ int j;
+ for (j = 0; j < sp->nchans; j++)
+ {
+ chan_info *cp;
+ cp = sp->chans[j];
+ if ((cp) && (selection_is_visible(cp)))
+ {
+ cp_redraw_selection(cp);
+ if ((cp->graph_transform_p) &&
+ (!(chan_fft_in_progress(cp))) &&
+ (show_selection_transform(ss)))
+ calculate_fft(cp);
+ if (sp->channel_style == CHANNELS_SUPERIMPOSED)
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void display_selection(chan_info *cp)
+{
+ if (selection_is_visible(cp))
+ cp_redraw_selection(cp); /* draw just this chan */
+}
+
+
+void update_possible_selection_in_progress(mus_long_t samp)
+{
+ if (syncd_chans)
+ {
+ int i;
+ mus_long_t original_beg;
+ if (samp < 0) samp = 0;
+ original_beg = syncd_chans->begs[0];
+ for (i = 0; i < syncd_chans->chans; i++)
+ {
+ chan_info *cp;
+ ed_list *ed;
+ mus_long_t new_end;
+ cp = syncd_chans->cps[i];
+ ed = cp->edits[cp->edit_ctr];
+ ed->selection_maxamp = -1.0;
+ ed->selection_maxamp_position = -1;
+ if (samp > CURRENT_SAMPLES(cp))
+ new_end = CURRENT_SAMPLES(cp);
+ else new_end = samp;
+ if (new_end < original_beg)
+ {
+ ed->selection_beg = new_end;
+ ed->selection_end = original_beg;
+ }
+ else
+ {
+ ed->selection_beg = original_beg;
+ ed->selection_end = new_end;
+ }
+ }
+ redraw_selection();
+ }
+}
+
+
+int make_region_from_selection(void)
+{
+ mus_long_t *ends = NULL;
+ int i, id = -1;
+ bool happy = false;
+ sync_info *si;
+ if (!(selection_is_active())) return(-1);
+ if (max_regions(ss) == 0) return(-1);
+ si = selection_sync();
+ ends = (mus_long_t *)calloc(si->chans, sizeof(mus_long_t));
+ for (i = 0; i < si->chans; i++)
+ {
+ ends[i] = selection_end(si->cps[i]);
+ if (ends[i] > si->begs[i]) happy = true;
+ /* C-space followed by mouse click causes a bogus selection-creation event */
+ }
+ if (happy) id = define_region(si, ends);
+ si = free_sync_info(si);
+ if (ends) free(ends);
+ return(id);
+}
+
+
+int select_all(chan_info *cp)
+{
+ if (cp)
+ {
+ sync_info *si;
+ int i;
+ deactivate_selection();
+ si = sync_to_chan(cp);
+ for (i = 0; i < si->chans; i++)
+ {
+ if (CURRENT_SAMPLES(si->cps[i]) > 0)
+ {
+ reactivate_selection(si->cps[i], 0, CURRENT_SAMPLES(si->cps[i]));
+ update_graph(si->cps[i]);
+ }
+ }
+ si = free_sync_info(si);
+ if ((selection_is_active()) && (selection_creates_region(ss)))
+ return(make_region_from_selection());
+ }
+ return(-1);
+}
+
+
+/* ---------------- selection mouse motion ---------------- */
+
+static int last_selection_x = 0;
+static timeout_result_t watch_selection_button = 0;
+
+void cancel_selection_watch(void)
+{
+ if (watch_selection_button)
+ TIMEOUT_REMOVE(watch_selection_button);
+ watch_selection_button = 0;
+}
+
+static void move_selection_1(chan_info *cp, int x);
+
+#if (!USE_NO_GUI)
+static TIMEOUT_TYPE watch_selection(TIMEOUT_ARGS)
+{
+ chan_info *cp = (chan_info *)context;
+ if (watch_selection_button)
+ {
+ move_selection_1(cp, last_selection_x);
+ watch_selection_button = CALL_TIMEOUT(watch_selection, 50, cp);
+ }
+ TIMEOUT_RESULT
+}
+#endif
+
+static void move_selection_1(chan_info *cp, int x)
+{
+ axis_info *ap;
+ ap = cp->axis;
+ if ((x > ap->x_axis_x1) || (x < ap->x_axis_x0))
+ {
+ if (((x > ap->x_axis_x1) && (ap->x1 == ap->xmax)) ||
+ ((x < ap->x_axis_x0) && (ap->x0 == ap->xmin)))
+ return;
+ move_axis(cp, ap, x);
+ if (!watch_selection_button)
+ watch_selection_button = CALL_TIMEOUT(watch_selection, 50, cp);
+ }
+ else
+ if (watch_selection_button)
+ cancel_selection_watch();
+ redraw_selection();
+}
+
+
+void move_selection(chan_info *cp, int x)
+{
+ last_selection_x = x; /* called in snd-xchn -- sets last_selection_x */
+ move_selection_1(cp, x);
+}
+
+
+io_error_t save_selection(const char *ofile, int type, int format, int srate, const char *comment, int chan)
+{
+ /* type and format have already been checked */
+ int ofd, bps;
+ io_error_t io_err = IO_NO_ERROR;
+ mus_long_t oloc;
+ sync_info *si = NULL;
+ mus_long_t *ends;
+ int i, j, k, chans;
+ mus_long_t dur, num, ioff;
+ snd_fd **sfs;
+ snd_info *sp = NULL;
+ mus_sample_t **data;
+
+ si = selection_sync();
+ if ((si) && (si->cps) && (si->cps[0])) sp = si->cps[0]->sound;
+
+ if (type == -1)
+ {
+ if ((sp) && (mus_header_writable(sp->hdr->type, -2))) /* -2 = ignore data format for the moment */
+ type = sp->hdr->type;
+ else type = MUS_NEXT;
+ }
+ if (format == -1)
+ {
+ if ((sp) && (mus_header_writable(type, sp->hdr->format)))
+ format = sp->hdr->format;
+ else format = MUS_OUT_FORMAT;
+ }
+ if (!mus_header_writable(type, format))
+ {
+ type = MUS_NEXT;
+ format = MUS_OUT_FORMAT;
+ }
+ if (srate == -1)
+ srate = selection_srate();
+
+ dur = selection_len();
+ if (chan == SAVE_ALL_CHANS)
+ chans = si->chans;
+ else chans = 1;
+
+ io_err = snd_write_header(ofile, type, srate, chans, chans * dur, format, comment, NULL);
+ ASSERT_IO_ERROR(io_err, "snd_write_header in save_selection");
+ if (io_err != IO_NO_ERROR)
+ {
+ si = free_sync_info(si);
+ return(io_err);
+ }
+
+ oloc = mus_header_data_location();
+ ofd = snd_reopen_write(ofile);
+
+ if (sp)
+ {
+ disk_space_t no_space;
+ bool copy_ok = false;
+
+ bps = mus_bytes_per_sample(format);
+ num = dur * bps * chans;
+
+ no_space = disk_space_p(num, ofile);
+ if (no_space != DISK_SPACE_OK)
+ {
+ snd_close(ofd, ofile);
+ si = free_sync_info(si);
+ return(IO_DISK_FULL);
+ }
+
+ copy_ok = ((format == sp->hdr->format) &&
+ (chans == sp->nchans) &&
+ (chan == SAVE_ALL_CHANS));
+ if (copy_ok)
+ for (i = 0; i < chans; i++)
+ if ((sp->chans[i]->edit_ctr != 0) ||
+ (si->cps[i]->sound != sp) ||
+ (si->begs[i] != si->begs[0]))
+ {
+ copy_ok = false;
+ break;
+ }
+ if (copy_ok)
+ {
+ /* write next header with correct len
+ * seek loc in sp->filename
+ * copy len*data-size bytes
+ * get max from amp envs
+ */
+ mus_long_t bytes, iloc;
+ int fdi;
+ char *buffer;
+
+ lseek(ofd, oloc, SEEK_SET);
+ fdi = mus_file_open_read(sp->filename); /* this does not read the header */
+ if (fdi == -1)
+ {
+ snd_close(ofd, ofile);
+ si = free_sync_info(si);
+ return(IO_CANT_READ_SELECTION_FILE);
+ }
+ /* snd_error(_("can't read selection's original sound? %s: %s"), sp->filename, snd_io_strerror()); */
+ else
+ {
+ iloc = mus_sound_data_location(sp->filename);
+ lseek(fdi, iloc + chans * bps * si->begs[0], SEEK_SET);
+ buffer = (char *)calloc(MAX_BUFFER_SIZE, sizeof(char));
+ for (j = 0; j < num; j += MAX_BUFFER_SIZE)
+ {
+ ssize_t n;
+ bytes = num - j;
+ if (bytes > MAX_BUFFER_SIZE) bytes = MAX_BUFFER_SIZE;
+ n = read(fdi, buffer, bytes);
+ if (n != 0)
+ n = write(ofd, buffer, bytes);
+ if (n == 0)
+ fprintf(stderr, "IO error while saving selection");
+ }
+ free(buffer);
+ snd_close(fdi, sp->filename);
+ }
+ snd_close(ofd, ofile);
+ si = free_sync_info(si);
+ if (!(ss->fam_ok))
+ alert_new_file();
+ return(IO_NO_ERROR);
+ }
+ }
+
+ ends = (mus_long_t *)calloc(chans, sizeof(mus_long_t));
+ sfs = (snd_fd **)calloc(chans, sizeof(snd_fd *));
+ if (chan == SAVE_ALL_CHANS)
+ for (i = 0; i < chans; i++)
+ {
+ ends[i] = selection_end(si->cps[i]);
+ sfs[i] = init_sample_read(selection_beg(si->cps[i]), si->cps[i], READ_FORWARD);
+ }
+ else
+ {
+ ends[0] = selection_end(si->cps[chan]);
+ sfs[0] = init_sample_read(selection_beg(si->cps[chan]), si->cps[chan], READ_FORWARD);
+ }
+
+ snd_file_open_descriptors(ofd, ofile, format, oloc, chans, type);
+ mus_file_set_clipping(ofd, clipping(ss));
+ lseek(ofd, oloc, SEEK_SET);
+ data = (mus_sample_t **)calloc(chans, sizeof(mus_sample_t *));
+ for (i = 0; i < chans; i++)
+ data[i] = (mus_sample_t *)calloc(FILE_BUFFER_SIZE, sizeof(mus_sample_t));
+
+ j = 0;
+ ss->stopped_explicitly = false;
+ for (ioff = 0; ioff < dur; ioff++)
+ {
+ for (k = 0; k < chans; k++)
+ {
+ if (ioff <= ends[k])
+ data[k][j] = read_sample_to_mus_sample(sfs[k]);
+ else data[k][j] = MUS_SAMPLE_0;
+ }
+ j++;
+ if (j == FILE_BUFFER_SIZE)
+ {
+ io_err = sndlib_error_to_snd(mus_file_write(ofd, 0, j - 1, chans, data));
+ j = 0;
+ if (io_err != IO_NO_ERROR)
+ {
+ snd_warning("%s %s: %s",
+ io_error_name(io_err),
+ ofile,
+ snd_io_strerror());
+ break;
+ }
+ if (ss->stopped_explicitly)
+ {
+ ss->stopped_explicitly = false;
+ snd_warning_without_format(_("save selection stopped"));
+ io_err = IO_INTERRUPTED;
+ break;
+ }
+ }
+ }
+ if ((io_err == IO_NO_ERROR) && (j > 0))
+ mus_file_write(ofd, 0, j - 1, chans, data);
+ for (i = 0; i < chans; i++)
+ {
+ free_snd_fd(sfs[i]);
+ free(data[i]);
+ }
+ free(sfs);
+ free(data);
+ si = free_sync_info(si);
+ free(ends);
+ if (mus_file_close(ofd) != 0)
+ return(IO_CANT_CLOSE_FILE);
+ if (!(ss->fam_ok))
+ alert_new_file();
+ return(io_err);
+}
+
+
+static XEN g_delete_selection(void)
+{
+ #define H_delete_selection "(" S_delete_selection "): delete the currently selected portion"
+ if (selection_is_active())
+ {
+ delete_selection(UPDATE_DISPLAY);
+ return(XEN_TRUE);
+ }
+ return(snd_no_active_selection_error(S_delete_selection));
+}
+
+
+static XEN g_insert_selection(XEN beg, XEN snd, XEN chn)
+{
+ #define H_insert_selection "(" S_insert_selection " :optional (beg 0) snd chn): insert the currently selected portion starting at beg"
+ if (selection_is_active())
+ {
+ chan_info *cp;
+ mus_long_t samp;
+ io_error_t io_err = IO_NO_ERROR;
+ sync_info *si_out;
+
+ ASSERT_CHANNEL(S_insert_selection, snd, chn, 2);
+ XEN_ASSERT_TYPE(XEN_NUMBER_IF_BOUND_P(beg), beg, XEN_ARG_1, S_insert_selection, "a number");
+
+ cp = get_cp(snd, chn, S_insert_selection);
+ if ((!cp) || (!(editable_p(cp)))) return(XEN_FALSE);
+
+ samp = beg_to_sample(beg, S_insert_selection);
+ if (XEN_NUMBER_P(chn))
+ si_out = make_simple_sync(cp, samp); /* ignore sync */
+ else si_out = sync_to_chan(cp);
+
+ io_err = insert_selection(cp, si_out, samp);
+ free_sync_info(si_out);
+
+ ASSERT_IO_ERROR(io_err, "insert_selection in g_insert_selection");
+ if (SERIOUS_IO_ERROR(io_err))
+ XEN_ERROR(XEN_ERROR_TYPE("IO-error"),
+ XEN_LIST_2(C_TO_XEN_STRING(S_insert_selection),
+ C_TO_XEN_STRING(io_error_name(io_err))));
+ return(XEN_FALSE);
+ }
+ return(snd_no_active_selection_error(S_insert_selection));
+}
+
+
+static XEN g_mix_selection(XEN beg, XEN snd, XEN chn, XEN sel_chan)
+{
+ #define H_mix_selection "(" S_mix_selection " :optional (beg 0) snd chn (selection-channel #t)): mix the currently selected portion starting at beg"
+ if (selection_is_active())
+ {
+ chan_info *cp;
+ mus_long_t obeg;
+ io_error_t io_err = IO_NO_ERROR;
+ int i, selection_chan = 0, id = -1, chans = 0;
+ sync_info *si_out;
+ XEN result = XEN_EMPTY_LIST;
+
+ ASSERT_CHANNEL(S_mix_selection, snd, chn, 2);
+ XEN_ASSERT_TYPE(XEN_NUMBER_IF_BOUND_P(beg), beg, XEN_ARG_1, S_mix_selection, "a number");
+ XEN_ASSERT_TYPE(XEN_INTEGER_OR_BOOLEAN_IF_BOUND_P(sel_chan), sel_chan, XEN_ARG_4, S_mix_selection, "an integer or #t");
+
+ cp = get_cp(snd, chn, S_mix_selection);
+ if ((!cp) || (!(editable_p(cp)))) return(XEN_FALSE);
+
+ obeg = beg_to_sample(beg, S_mix_selection);
+ if (XEN_INTEGER_P(sel_chan))
+ selection_chan = XEN_TO_C_INT(sel_chan);
+ if (XEN_NUMBER_P(chn))
+ si_out = make_simple_sync(cp, obeg); /* ignore sync */
+ else si_out = sync_to_chan(cp);
+
+ chans = si_out->chans;
+ id = mix_selection(cp, si_out, obeg, &io_err, selection_chan);
+ free_sync_info(si_out);
+
+ if (SERIOUS_IO_ERROR(io_err))
+ XEN_ERROR(XEN_ERROR_TYPE("IO-error"),
+ XEN_LIST_2(C_TO_XEN_STRING(S_mix_selection),
+ C_TO_XEN_STRING(io_error_name(io_err))));
+
+ if (id == -1) return(XEN_FALSE);
+ for (i = 0; i < chans; i++)
+ result = XEN_CONS(new_xen_mix(id + i), result);
+ return(XEN_LIST_REVERSE(result));
+ }
+ return(snd_no_active_selection_error(S_mix_selection));
+}
+
+
+static XEN g_selection_p(void)
+{
+ #define H_selection_p "(" S_selection_p "): " PROC_TRUE " if selection is currently active, visible, etc"
+ return(C_TO_XEN_BOOLEAN(selection_is_active()));
+}
+
+
+static XEN g_selection_position(XEN snd, XEN chn)
+{
+ #define H_selection_position "(" S_selection_position " :optional snd chn): selection start samp"
+ if (selection_is_active())
+ {
+ if (XEN_NOT_BOUND_P(snd))
+ return(C_TO_XEN_INT64_T(selection_beg(NULL)));
+ else
+ {
+ chan_info *cp;
+ ASSERT_CHANNEL(S_selection_position, snd, chn, 1);
+ cp = get_cp(snd, chn, S_selection_position);
+ if (!cp) return(XEN_FALSE);
+ return(C_TO_XEN_INT64_T(selection_beg(cp)));
+ }
+ }
+ return(snd_no_active_selection_error(S_selection_position));
+}
+
+
+static XEN g_set_selection_position(XEN pos, XEN snd, XEN chn)
+{
+ chan_info *cp;
+ mus_long_t beg;
+
+ ASSERT_CHANNEL(S_setB S_selection_position, snd, chn, 2);
+ XEN_ASSERT_TYPE(XEN_NUMBER_P(pos), pos, XEN_ARG_1, S_selection_position, "a number");
+
+ beg = beg_to_sample(pos, S_setB S_selection_position);
+ if (XEN_NOT_BOUND_P(snd))
+ {
+ sync_info *si = NULL;
+ if (selection_is_active())
+ si = selection_sync();
+ else
+ {
+ cp = current_channel();
+ if (cp) si = sync_to_chan(cp);
+ }
+ if (si)
+ {
+ int i;
+ for (i = 0; i < si->chans; i++)
+ cp_set_selection_beg(si->cps[i], beg);
+ si = free_sync_info(si);
+ }
+ }
+ else
+ {
+ cp = get_cp(snd, chn, S_setB S_selection_position);
+ if (!cp) return(XEN_FALSE);
+ cp_set_selection_beg(cp, beg);
+ }
+ redraw_selection();
+ return(pos);
+}
+
+WITH_THREE_SETTER_ARGS(g_set_selection_position_reversed, g_set_selection_position)
+
+
+static XEN g_selection_frames(XEN snd, XEN chn)
+{
+ #define H_selection_frames "(" S_selection_frames " :optional snd chn): selection length"
+ if (selection_is_active())
+ {
+ if (XEN_NOT_BOUND_P(snd))
+ return(C_TO_XEN_INT64_T(selection_len()));
+ else
+ {
+ chan_info *cp;
+ ASSERT_CHANNEL(S_selection_frames, snd, chn, 1);
+ cp = get_cp(snd, chn, S_selection_frames);
+ if (!cp) return(XEN_FALSE);
+ return(C_TO_XEN_INT64_T(cp_selection_len(cp, NULL)));
+ }
+ }
+ return(snd_no_active_selection_error(S_selection_frames));
+}
+
+
+static XEN g_set_selection_frames(XEN samps, XEN snd, XEN chn)
+{
+ chan_info *cp;
+ mus_long_t len;
+
+ XEN_ASSERT_TYPE(XEN_NUMBER_P(samps), samps, XEN_ARG_1, S_setB S_selection_frames, "a number");
+ len = XEN_TO_C_INT64_T_OR_ELSE(samps, 0);
+ if (len <= 0)
+ XEN_WRONG_TYPE_ARG_ERROR(S_setB S_selection_frames, XEN_ARG_1, samps, "a positive integer");
+ if (XEN_NOT_BOUND_P(snd))
+ {
+ sync_info *si = NULL;
+ if (selection_is_active())
+ si = selection_sync();
+ else
+ {
+ cp = current_channel();
+ if (cp) si = sync_to_chan(cp);
+ }
+ if (si)
+ {
+ int i;
+ for (i = 0; i < si->chans; i++)
+ cp_set_selection_len(si->cps[i], len);
+ si = free_sync_info(si);
+ }
+ }
+ else
+ {
+ ASSERT_CHANNEL(S_setB S_selection_frames, snd, chn, 2);
+ cp = get_cp(snd, chn, S_setB S_selection_frames);
+ if (!cp) return(XEN_FALSE);
+ cp_set_selection_len(cp, len);
+ }
+ redraw_selection();
+ return(samps);
+}
+
+WITH_THREE_SETTER_ARGS(g_set_selection_frames_reversed, g_set_selection_frames)
+
+
+static XEN g_selection_member(XEN snd, XEN chn)
+{
+ #define H_selection_member "(" S_selection_member " :optional snd chn): " PROC_TRUE " if snd's channel chn is a member of the current selection"
+ chan_info *cp;
+ ASSERT_CHANNEL(S_selection_member, snd, chn, 1);
+ cp = get_cp(snd, chn, S_selection_member);
+ if (!cp) return(XEN_FALSE);
+ return(C_TO_XEN_BOOLEAN(selection_is_active_in_channel(cp)));
+}
+
+
+static XEN g_set_selection_member(XEN on, XEN snd, XEN chn)
+{
+ XEN_ASSERT_TYPE(XEN_BOOLEAN_P(on), on, XEN_ARG_1, S_setB S_selection_member, "a boolean");
+ if ((XEN_TRUE_P(snd)) && (XEN_FALSE_P(on)))
+ deactivate_selection();
+ else
+ {
+ chan_info *cp;
+ ASSERT_CHANNEL(S_setB S_selection_member, snd, chn, 2);
+ cp = get_cp(snd, chn, S_setB S_selection_member);
+ if (!cp) return(XEN_FALSE);
+ if (XEN_TRUE_P(on))
+ {
+ if (selection_is_active())
+ cp_set_selection_beg(cp, selection_beg(NULL));
+ else cp_set_selection_beg(cp, 0);
+ }
+ else cp_deactivate_selection(cp);
+ call_selection_watchers(SELECTION_CHANGED);
+ if (selection_is_active())
+ redraw_selection();
+ }
+ return(on);
+}
+
+WITH_THREE_SETTER_ARGS(g_set_selection_member_reversed, g_set_selection_member)
+
+
+static XEN g_select_all(XEN snd_n, XEN chn_n)
+{
+ #define H_select_all "(" S_select_all " :optional snd chn): make a new selection containing all of snd's channel chn. \
+If sync is set, all chans are included. The new region id is returned (if " S_selection_creates_region " is " PROC_TRUE ")."
+ chan_info *cp;
+ int id;
+
+ ASSERT_CHANNEL(S_select_all, snd_n, chn_n, 1);
+ cp = get_cp(snd_n, chn_n, S_select_all);
+ if (!cp) return(XEN_FALSE);
+
+ id = select_all(cp);
+ if (selection_creates_region(ss))
+ return(C_INT_TO_XEN_REGION(id));
+ else return(XEN_FALSE);
+}
+
+
+static XEN kw_header_type, kw_data_format, kw_comment, kw_file, kw_srate, kw_channel;
+
+static void init_selection_keywords(void)
+{
+ kw_header_type = XEN_MAKE_KEYWORD("header-type");
+ kw_data_format = XEN_MAKE_KEYWORD("data-format");
+ kw_comment = XEN_MAKE_KEYWORD("comment");
+ kw_file = XEN_MAKE_KEYWORD("file");
+ kw_srate = XEN_MAKE_KEYWORD("srate");
+ kw_channel = XEN_MAKE_KEYWORD("channel");
+}
+
+
+static XEN g_save_selection(XEN arglist)
+{
+ #define H_save_selection "(" S_save_selection " :file :header-type :data-format :srate :comment :channel): \
+save the current selection in file using the indicated file attributes. If channel is given, save only that channel."
+
+ int type = -1, format = -1, sr = -1, chn = 0;
+ io_error_t io_err = IO_NO_ERROR;
+ const char *com = NULL, *file = NULL;
+ char *fname = NULL;
+ XEN args[12];
+ XEN keys[6];
+ int orig_arg[6] = {0, 0, 0, 0, 0, 0};
+ int vals, i, arglist_len;
+ if (!(selection_is_active()))
+ return(snd_no_active_selection_error(S_save_selection));
+
+ /* changed 7-Dec-08 to be more like save-sound-as in default values -- if just one
+ * sound involved, or all sounds same, use current choices rather than MUS_NEXT etc:
+ * hdr->type|srate|format
+ */
+
+ keys[0] = kw_file;
+ keys[1] = kw_header_type;
+ keys[2] = kw_data_format;
+ keys[3] = kw_srate;
+ keys[4] = kw_comment;
+ keys[5] = kw_channel;
+ for (i = 0; i < 12; i++) args[i] = XEN_UNDEFINED;
+ arglist_len = XEN_LIST_LENGTH(arglist);
+ for (i = 0; i < arglist_len; i++) args[i] = XEN_LIST_REF(arglist, i);
+ vals = mus_optkey_unscramble(S_save_selection, 6, keys, args, orig_arg);
+ if (vals > 0)
+ {
+ file = mus_optkey_to_string(keys[0], S_save_selection, orig_arg[0], NULL);
+ type = mus_optkey_to_int(keys[1], S_save_selection, orig_arg[1], type);
+ format = mus_optkey_to_int(keys[2], S_save_selection, orig_arg[2], format);
+ sr = mus_optkey_to_int(keys[3], S_save_selection, orig_arg[3], selection_srate());
+ com = mus_optkey_to_string(keys[4], S_save_selection, orig_arg[4], NULL);
+ chn = mus_optkey_to_int(keys[5], S_save_selection, orig_arg[5], SAVE_ALL_CHANS);
+ }
+ if (file == NULL)
+ XEN_ERROR(XEN_ERROR_TYPE("IO-error"),
+ XEN_LIST_2(C_TO_XEN_STRING(S_save_selection),
+ C_TO_XEN_STRING("no output file?")));
+ if ((type != -1) && (!(mus_header_writable(type, -2))))
+ XEN_ERROR(CANNOT_SAVE,
+ XEN_LIST_3(C_TO_XEN_STRING(S_save_selection),
+ C_TO_XEN_STRING(_("can't write this header type:")),
+ C_TO_XEN_STRING(mus_header_type_name(type))));
+ if ((type != -1) && (format != -1) && (!(mus_header_writable(type, format))))
+ XEN_ERROR(CANNOT_SAVE,
+ XEN_LIST_4(C_TO_XEN_STRING(S_save_selection),
+ C_TO_XEN_STRING(_("can't write this combination of header type and data format:")),
+ C_TO_XEN_STRING(mus_header_type_name(type)),
+ C_TO_XEN_STRING(mus_data_format_name(format))));
+ if ((sr != -1) && (sr <= 0))
+ XEN_ERROR(CANNOT_SAVE,
+ XEN_LIST_3(C_TO_XEN_STRING(S_save_selection),
+ C_TO_XEN_STRING(_("srate can't be <= 0")),
+ C_TO_XEN_INT(sr)));
+ fname = mus_expand_filename(file);
+ io_err = save_selection(fname, type, format, sr, com, chn);
+ if (fname) free(fname);
+ if ((io_err != IO_NO_ERROR) &&
+ (io_err != IO_INTERRUPTED) &&
+ (io_err != IO_SAVE_HOOK_CANCELLATION))
+ XEN_ERROR(CANNOT_SAVE,
+ XEN_LIST_2(C_TO_XEN_STRING(S_save_selection),
+ C_TO_XEN_STRING(snd_open_strerror())));
+ return(args[orig_arg[0] - 1]);
+}
+
+
+static XEN g_selection_chans(void)
+{
+ #define H_selection_chans "(" S_selection_chans "): chans in active selection"
+ return(C_TO_XEN_INT(selection_chans()));
+}
+
+
+static XEN g_selection_srate(void)
+{
+ #define H_selection_srate "(" S_selection_srate "): selection srate"
+ return(C_TO_XEN_INT(selection_srate()));
+}
+
+
+static XEN g_selection_maxamp(XEN snd, XEN chn)
+{
+ #define H_selection_maxamp "(" S_selection_maxamp " :optional snd chn): selection maxamp in given channel"
+ chan_info *cp;
+ ASSERT_CHANNEL(S_selection_maxamp, snd, chn, 1);
+ cp = get_cp(snd, chn, S_selection_maxamp);
+ if (!cp) return(XEN_FALSE);
+ return(C_TO_XEN_DOUBLE(selection_maxamp(cp)));
+}
+
+
+static XEN g_selection_maxamp_position(XEN snd, XEN chn)
+{
+ #define H_selection_maxamp_position "(" S_selection_maxamp_position " :optional snd chn): location of selection maxamp (0 = start of selection)"
+ chan_info *cp;
+ ASSERT_CHANNEL(S_selection_maxamp_position, snd, chn, 1);
+ cp = get_cp(snd, chn, S_selection_maxamp_position);
+ if (!cp) return(XEN_FALSE);
+ return(C_TO_XEN_INT64_T(selection_maxamp_position(cp)));
+}
+
+
+#ifdef XEN_ARGIFY_1
+XEN_ARGIFY_2(g_selection_position_w, g_selection_position)
+XEN_ARGIFY_3(g_set_selection_position_w, g_set_selection_position)
+XEN_ARGIFY_2(g_selection_frames_w, g_selection_frames)
+XEN_ARGIFY_3(g_set_selection_frames_w, g_set_selection_frames)
+XEN_ARGIFY_2(g_selection_member_w, g_selection_member)
+XEN_ARGIFY_3(g_set_selection_member_w, g_set_selection_member)
+XEN_NARGIFY_0(g_selection_p_w, g_selection_p)
+XEN_NARGIFY_0(g_selection_chans_w, g_selection_chans)
+XEN_NARGIFY_0(g_selection_srate_w, g_selection_srate)
+XEN_ARGIFY_2(g_selection_maxamp_w, g_selection_maxamp)
+XEN_ARGIFY_2(g_selection_maxamp_position_w, g_selection_maxamp_position)
+XEN_NARGIFY_0(g_delete_selection_w, g_delete_selection)
+XEN_ARGIFY_3(g_insert_selection_w, g_insert_selection)
+XEN_ARGIFY_4(g_mix_selection_w, g_mix_selection)
+XEN_ARGIFY_2(g_select_all_w, g_select_all)
+XEN_VARGIFY(g_save_selection_w, g_save_selection)
+#else
+#define g_selection_position_w g_selection_position
+#define g_set_selection_position_w g_set_selection_position
+#define g_selection_frames_w g_selection_frames
+#define g_set_selection_frames_w g_set_selection_frames
+#define g_selection_member_w g_selection_member
+#define g_set_selection_member_w g_set_selection_member
+#define g_selection_p_w g_selection_p
+#define g_selection_chans_w g_selection_chans
+#define g_selection_srate_w g_selection_srate
+#define g_selection_maxamp_w g_selection_maxamp
+#define g_selection_maxamp_position_w g_selection_maxamp_position
+#define g_delete_selection_w g_delete_selection
+#define g_insert_selection_w g_insert_selection
+#define g_mix_selection_w g_mix_selection
+#define g_select_all_w g_select_all
+#define g_save_selection_w g_save_selection
+#endif
+
+void g_init_selection(void)
+{
+ init_selection_keywords();
+
+ XEN_DEFINE_PROCEDURE_WITH_REVERSED_SETTER(S_selection_position, g_selection_position_w, H_selection_position,
+ S_setB S_selection_position, g_set_selection_position_w, g_set_selection_position_reversed,
+ 0, 2, 1, 2);
+
+ XEN_DEFINE_PROCEDURE_WITH_REVERSED_SETTER(S_selection_frames, g_selection_frames_w, H_selection_frames,
+ S_setB S_selection_frames, g_set_selection_frames_w, g_set_selection_frames_reversed,
+ 0, 2, 1, 2);
+
+ XEN_DEFINE_PROCEDURE_WITH_REVERSED_SETTER(S_selection_member, g_selection_member_w, H_selection_member,
+ S_setB S_selection_member, g_set_selection_member_w, g_set_selection_member_reversed,
+ 0, 2, 1, 2);
+
+ XEN_DEFINE_PROCEDURE(S_selection_p, g_selection_p_w, 0, 0, 0, H_selection_p);
+ XEN_DEFINE_PROCEDURE(S_selection_chans, g_selection_chans_w, 0, 0, 0, H_selection_chans);
+ XEN_DEFINE_PROCEDURE(S_selection_srate, g_selection_srate_w, 0, 0, 0, H_selection_srate);
+ XEN_DEFINE_PROCEDURE(S_selection_maxamp, g_selection_maxamp_w, 0, 2, 0, H_selection_maxamp);
+ XEN_DEFINE_PROCEDURE(S_selection_maxamp_position, g_selection_maxamp_position_w, 0, 2, 0, H_selection_maxamp_position);
+ XEN_DEFINE_PROCEDURE(S_delete_selection, g_delete_selection_w, 0, 0, 0, H_delete_selection);
+ XEN_DEFINE_PROCEDURE(S_insert_selection, g_insert_selection_w, 0, 3, 0, H_insert_selection);
+ XEN_DEFINE_PROCEDURE(S_mix_selection, g_mix_selection_w, 0, 4, 0, H_mix_selection);
+ XEN_DEFINE_PROCEDURE(S_select_all, g_select_all_w, 0, 2, 0, H_select_all);
+ XEN_DEFINE_PROCEDURE(S_save_selection, g_save_selection_w, 0, 0, 1, H_save_selection);
+}