1 #include "snd.h"
2 
3 struct mark {
4   mus_long_t samp;
5   char *name;
6   int id, sync;
7   bool visible;
8   Xen properties;
9   int properties_gc_loc;
10 };
11 
12 static int mark_id_counter = 0;
13 
mark_to_int(mark * m)14 int mark_to_int(mark *m) {return(m->id);}
15 
16 
mark_sample(mark * m)17 mus_long_t mark_sample(mark *m) {return(m->samp);}
18 
19 
20 static int sync_max = 0;
21 
mark_sync_max(void)22 int mark_sync_max(void)
23 {
24   return(sync_max);
25 }
26 
27 
mark_sync(mark * m)28 int mark_sync(mark *m) {return(m->sync);}
29 
set_mark_sync(mark * m,int val)30 void set_mark_sync(mark *m, int val)
31 {
32   m->sync = val;
33   if (val > sync_max)
34     sync_max = val;
35 }
36 
37 
make_mark_1(mus_long_t samp,const char * name,int id,int sc)38 static mark *make_mark_1(mus_long_t samp, const char *name, int id, int sc)
39 {
40   mark *mp;
41   mp = (mark *)calloc(1, sizeof(mark));
42   if (name) mp->name = mus_strdup(name); else mp->name = NULL;
43   mp->samp = samp;
44   mp->id = id;
45   set_mark_sync(mp, sc);
46   mp->properties_gc_loc = NOT_A_GC_LOC;
47   mp->properties = Xen_false;
48   return(mp);
49 }
50 
51 
make_mark(mus_long_t samp,const char * name)52 static mark *make_mark(mus_long_t samp, const char *name)
53 {
54   return(make_mark_1(samp, name, mark_id_counter++, 0));
55 }
56 
57 
copy_mark(mark * m)58 static mark *copy_mark(mark *m)
59 {
60   return(make_mark_1(m->samp, m->name, m->id, m->sync));
61 }
62 
63 
free_mark(mark * mp)64 static mark *free_mark(mark *mp)
65 {
66   if (mp)
67     {
68       if (mp->name) free(mp->name);
69       if (mp->properties_gc_loc != NOT_A_GC_LOC)
70 	{
71 	  snd_unprotect_at(mp->properties_gc_loc);
72 	  mp->properties_gc_loc = NOT_A_GC_LOC;
73 	  mp->properties = Xen_false;
74 	}
75       free(mp);
76     }
77   return(NULL);
78 }
79 
80 
map_over_marks(chan_info * cp,mark * (* func)(chan_info * ncp,mark * mp1,void * p1),void * m,read_direction_t direction)81 static mark *map_over_marks(chan_info *cp, mark *(*func)(chan_info *ncp, mark *mp1, void *p1), void *m, read_direction_t direction)
82 {
83   ed_list *ed;
84   ed = cp->edits[cp->edit_ctr];
85   if ((ed) && (ed->marks))
86     {
87       int marks;
88       mark **mps;
89       mps = ed->marks;
90       marks = ed->mark_ctr;
91       if (mps)
92 	{
93 	  mark *mp;
94 	  int i;
95 	  if (direction == READ_FORWARD)
96 	    {
97 	      for (i = 0; i <= marks; i++)
98 		if (mps[i]) /* can be null if we're running delete_marks at a higher level and draw-mark-hook is active */
99 		  {
100 		    mp = (*func)(cp, mps[i], m);
101 		    if (mp) return(mp);
102 		  }
103 	    }
104 	  else
105 	    {
106 	      for (i = marks; i >= 0; i--)
107 		if (mps[i])
108 		  {
109 		    mp = (*func)(cp, mps[i], m);
110 		    if (mp) return(mp);
111 		  }
112 	    }
113 	}
114     }
115   return(NULL);
116 }
117 
118 
map_over_marks_with_int(chan_info * cp,mark * (* func)(chan_info * ncp,mark * mp1,int val1),int value,read_direction_t direction)119 static mark *map_over_marks_with_int(chan_info *cp, mark *(*func)(chan_info *ncp, mark *mp1, int val1), int value, read_direction_t direction)
120 {
121   ed_list *ed;
122   ed = cp->edits[cp->edit_ctr];
123   if ((ed) && (ed->marks))
124     {
125       int marks;
126       mark **mps;
127       mps = ed->marks;
128       marks = ed->mark_ctr;
129       if (mps)
130 	{
131 	  mark *mp;
132 	  int i;
133 	  if (direction == READ_FORWARD)
134 	    {
135 	      for (i = 0; i <= marks; i++)
136 		if (mps[i]) /* can be null if we're running delete_marks at a higher level and draw-mark-hook is active */
137 		  {
138 		    mp = (*func)(cp, mps[i], value);
139 		    if (mp) return(mp);
140 		  }
141 	    }
142 	  else
143 	    {
144 	      for (i = marks; i >= 0; i--)
145 		if (mps[i])
146 		  {
147 		    mp = (*func)(cp, mps[i], value);
148 		    if (mp) return(mp);
149 		  }
150 	    }
151 	}
152     }
153   return(NULL);
154 }
155 
156 
find_mark_id_1(chan_info * cp,mark * mp,int id)157 static mark *find_mark_id_1(chan_info *cp, mark *mp, int id)
158 {
159   if (mp->id == id)
160     return(mp);
161   return(NULL);
162 }
163 
164 
find_mark_from_id(int id,chan_info ** cps,int pos)165 static mark *find_mark_from_id(int id, chan_info **cps, int pos)
166 {
167   int i;
168   for (i = 0; i < ss->max_sounds; i++)
169     {
170       chan_info *cp;
171       snd_info *sp;
172       uint32_t j;
173       sp = ss->sounds[i];
174       if ((sp) && (sp->inuse == SOUND_NORMAL))
175 	for (j = 0; j < sp->nchans; j++)
176 	  if ((cp = ((chan_info *)(sp->chans[j]))))
177 	    {
178 	      if (pos < cp->edit_size) /* pos can be -1 */
179 		{
180 		  int old_pos;
181 		  mark *mp;
182 		  old_pos = cp->edit_ctr;
183 		  if (pos >= 0) cp->edit_ctr = pos;
184 		  /* memoization would have to be done here where we know cp->edit_ctr */
185 		  mp = map_over_marks_with_int(cp, find_mark_id_1, id, READ_FORWARD);
186 		  cp->edit_ctr = old_pos;
187 		  if (mp)
188 		    {
189 		      if (cps) cps[0] = cp;
190 		      return(mp);
191 		    }
192 		}
193 	      }
194 	  }
195   return(NULL);
196 }
197 
198 
mark_id_to_sample(int id)199 mus_long_t mark_id_to_sample(int id)
200 {
201   mark *m;
202   m = find_mark_from_id(id, NULL, AT_CURRENT_EDIT_POSITION);
203   if (m)
204     return(m->samp);
205   return(-1);
206 }
207 
208 
find_previous_mark_1(chan_info * cp,mark * mp,void * m)209 static mark *find_previous_mark_1(chan_info *cp, mark *mp, void *m)
210 {
211   if (mp->samp < (*((mus_long_t *)m)))
212     return(mp);
213   return(NULL);
214 }
215 
216 
find_previous_mark(mus_long_t current_sample,chan_info * cp)217 static mark *find_previous_mark(mus_long_t current_sample, chan_info *cp)
218 {
219   return(map_over_marks(cp, find_previous_mark_1, (void *)(&current_sample), READ_BACKWARD));
220 }
221 
222 
find_next_mark_1(chan_info * cp,mark * mp,void * m)223 static mark *find_next_mark_1(chan_info *cp, mark *mp, void *m)
224 {
225   if (mp->samp > (*((mus_long_t *)m)))
226     return(mp);
227   return(NULL);
228 }
229 
230 
find_next_mark(mus_long_t current_sample,chan_info * cp)231 static mark *find_next_mark(mus_long_t current_sample, chan_info *cp)
232 {
233   return(map_over_marks(cp, find_next_mark_1, (void *)(&current_sample), READ_FORWARD));
234 }
235 
236 
marks_off_1(chan_info * cp,mark * mp,void * ignore)237 static mark* marks_off_1(chan_info *cp, mark *mp, void *ignore)
238 {
239   mp->visible = false;
240   return(NULL);
241 }
242 
243 
marks_off(chan_info * cp)244 void marks_off(chan_info *cp)
245 {
246   map_over_marks(cp, marks_off_1, NULL, READ_FORWARD);
247 }
248 
249 
250 static void show_mark(chan_info *cp, mark *mp, bool show);
251 
252 static Xen draw_mark_hook;
253 
draw_mark_1(chan_info * cp,mark * mp,bool show)254 static void draw_mark_1(chan_info *cp, mark *mp, bool show)
255 {
256   /* fields are samp and name */
257   if (!(cp->graph_time_on)) return;
258   if (Xen_hook_has_list(draw_mark_hook))
259     {
260       Xen res;
261       res = run_progn_hook(draw_mark_hook,
262 			   Xen_list_1(new_xen_mark(mp->id)),
263 			   S_draw_mark_hook);
264       if (Xen_is_true(res))
265 	{
266 	  mp->visible = show;
267 	  return;
268 	}
269     }
270   show_mark(cp, mp, show);
271 }
272 
273 
draw_mark(chan_info * cp,mark * mp)274 static void draw_mark(chan_info *cp, mark *mp)
275 {
276   if (!(mp->visible)) draw_mark_1(cp, mp, true);
277 }
278 
279 
erase_mark(chan_info * cp,mark * mp)280 static void erase_mark(chan_info *cp, mark *mp)
281 {
282   if (mp->visible) draw_mark_1(cp, mp, false);
283 }
284 
285 
286 typedef struct {
287   int x, y;
288   mark *all_done;
289 } mdata;
290 
hit_mark_1(chan_info * cp,mark * mp,void * m)291 static mark *hit_mark_1(chan_info *cp, mark *mp, void *m)
292 {
293   int mx;
294   mdata *md = (mdata *)m;
295   axis_info *ap;
296 
297   ap = cp->axis;
298   if (mp->samp < ap->losamp) return(NULL);
299   if (mp->samp > ap->hisamp) return(md->all_done); /* grf_x clips so we can be confused by off-screen marks */
300 
301   mx = grf_x((double)(mp->samp) / (double)snd_srate(cp->sound), cp->axis);
302   if (mx > (md->x + mark_tag_width(ss))) return(md->all_done); /* past it */
303   if (mx < (md->x - mark_tag_width(ss))) return(NULL);         /* before it */
304   if (!mp->name)                                    /* check y if unnamed */
305     {
306       if ((md->y >= ap->y_axis_y1) &&
307 	  (md->y <= (ap->y_axis_y1 + mark_tag_height(ss))))
308 	return(mp);
309       else return(NULL);
310     }
311   else return(mp);
312 }
313 
314 
315 #define HIT_SLOP 4
316 
hit_mark_triangle_1(chan_info * cp,mark * mp,void * m)317 static mark *hit_mark_triangle_1(chan_info *cp, mark *mp, void *m)
318 {
319   /* m->samp = raw x mouse position */
320   /* we're going left to right, so after passing x, give up */
321   int mx, y;
322   mdata *md = (mdata *)m;
323   axis_info *ap;
324 
325   ap = cp->axis;
326   if (mp->samp < ap->losamp) return(NULL);
327   if (mp->samp > ap->hisamp) return(md->all_done); /* grf_x clips so we can be confused by off-screen marks */
328 
329   mx = grf_x((double)(mp->samp) / (double)snd_srate(cp->sound), cp->axis);
330   if (mx > (md->x + HIT_SLOP)) return(md->all_done);
331   if ((mx + play_arrow_size(ss) + HIT_SLOP) < md->x) return(NULL);
332   y = md->y - ap->y_axis_y0 - play_arrow_size(ss);
333   if (y < 0) y = -y;
334   if ((mx + play_arrow_size(ss) - y + HIT_SLOP) >= md->x) return(mp);
335   /* the last is assuming the triangle shape for hit detection */
336   return(NULL);
337 }
338 
339 
hit_mark_triangle(chan_info * cp,int x,int y)340 mark *hit_mark_triangle(chan_info *cp, int x, int y)
341 {
342   if (cp->edits[cp->edit_ctr]->marks)
343     {
344       axis_info *ap;
345       ap = cp->axis;
346       /* first check that we're in the bottom portion of the graph where the mark triangles are */
347       if ((y >= ap->y_axis_y0) &&
348 	  (y <= (ap->y_axis_y0 + 2 * play_arrow_size(ss))))
349 	{
350 	  mark *mp;
351 	  mdata *md;
352 	  md = (mdata *)calloc(2, sizeof(mdata));
353 	  md->x = x;
354 	  md->y = y;
355 	  md->all_done = (mark *)1;
356 	  mp = map_over_marks(cp, hit_mark_triangle_1, (void *)md, READ_FORWARD);
357 	  if (mp == (mark *)1) mp = NULL;
358 	  free(md);
359 	  return(mp);
360 	}
361     }
362   return(NULL);
363 }
364 
365 
366 static Xen mark_drag_hook;
367 static Xen mark_hook; /* add, delete, move */
368 
369 static bool watching_mouse = false; /* this is tracking axis moves */
370 static int last_mouse_x = 0;
371 static mark *moving_mark = NULL; /* used only while "off-screen" during axis moves */
372 
373 static timeout_result_t watch_mouse_button = 0;
374 
375 #if (!USE_NO_GUI)
376 static void move_axis_to_track_mark(chan_info *cp);
377 
watch_mouse(TIMEOUT_ARGS)378 static TIMEOUT_TYPE watch_mouse(TIMEOUT_ARGS)
379 {
380   chan_info *cp = (chan_info *)context;
381   if (watch_mouse_button)
382     {
383       move_axis_to_track_mark(cp);
384       watch_mouse_button = CALL_TIMEOUT(watch_mouse, 50, cp);
385     }
386   TIMEOUT_RESULT
387 }
388 #endif
389 
390 
start_mark_watching(chan_info * cp,mark * mp)391 static void start_mark_watching(chan_info *cp, mark *mp)
392 {
393   moving_mark = mp;
394   watch_mouse_button = CALL_TIMEOUT(watch_mouse, 50, cp);
395   watching_mouse = true;
396 }
397 
398 
cancel_mark_watch(chan_info * cp)399 static void cancel_mark_watch(chan_info *cp)
400 {
401 #if (!USE_NO_GUI)
402   if (watch_mouse_button) TIMEOUT_REMOVE(watch_mouse_button);
403 #endif
404   watch_mouse_button = 0;
405   watching_mouse = false;
406   moving_mark = NULL;
407 }
408 
409 
move_mark_1(chan_info * cp,mark * mp,int x)410 static bool move_mark_1(chan_info *cp, mark *mp, int x)
411 {
412   axis_info *ap;
413   int nx;
414   mus_long_t samps;
415   bool redraw;
416 
417   ap = cp->axis;
418   redraw = (!watching_mouse);
419 
420   if ((x > ap->x_axis_x1) || (x < ap->x_axis_x0))
421     {
422       if (watching_mouse)
423 	{
424 	  if (((x < ap->x_axis_x0) && (ap->x0 == ap->xmin)) ||
425 	      ((x > ap->x_axis_x1) && (ap->x1 == ap->xmax)))
426 	    return(false);
427 	}
428       nx = move_axis(cp, x);
429       if (!watching_mouse) start_mark_watching(cp, mp);
430     }
431   else
432     {
433       erase_mark(cp, mp);
434       nx = x;
435       if (watching_mouse)
436 	{
437 	  cancel_mark_watch(cp);
438 	  redraw = false;
439 	}
440     }
441 
442   mp->samp = (mus_long_t)(ungrf_x(ap, nx) * snd_srate(cp->sound));
443   if (mp->samp < 0) mp->samp = 0;
444 
445   samps = current_samples(cp);
446   if (mp->samp >= samps) mp->samp = samps - 1;
447 
448   if (Xen_hook_has_list(mark_drag_hook))
449     ss->squelch_mark_drag_info = Xen_is_true(run_progn_hook(mark_drag_hook,
450 							   Xen_list_1(new_xen_mark(mp->id)),
451 							   S_mark_drag_hook));
452   else ss->squelch_mark_drag_info = false;
453   return(redraw);
454 }
455 
456 
compare_mark_samps(const void * mp1,const void * mp2)457 static int compare_mark_samps(const void *mp1, const void *mp2)
458 {
459   mark *m1, *m2;
460   m1 = (mark *)(*((mark **)mp1));
461   m2 = (mark *)(*((mark **)mp2));
462   if (m1->samp < m2->samp) return(-1);
463   if (m1->samp == m2->samp) return(0);
464   return(1);
465 }
466 
467 
sort_marks(chan_info * cp)468 static void sort_marks(chan_info *cp)
469 {
470   ed_list *ed;
471   ed = cp->edits[cp->edit_ctr];
472   if (ed)
473     qsort((void *)(ed->marks), ed->mark_ctr + 1, sizeof(mark *), compare_mark_samps);
474 }
475 
476 
477 typedef enum {MARK_ADD, MARK_DELETE, MARK_MOVE, MARKS_DELETE, MARK_RELEASE} mark_hook_reason_t;
478 
run_mark_hook(chan_info * cp,int id,mark_hook_reason_t reason)479 static void run_mark_hook(chan_info *cp, int id, mark_hook_reason_t reason)
480 {
481   /* called after the mark list has been made consistent */
482   if (Xen_hook_has_list(mark_hook))
483     run_hook(mark_hook,
484 	     Xen_list_4(new_xen_mark(id),
485 			C_int_to_Xen_sound(cp->sound->index),
486 			C_int_to_Xen_integer(cp->chan),
487 			C_int_to_Xen_integer((int)reason)),
488 	     S_mark_hook);
489 
490   if (Xen_hook_has_list(ss->effects_hook))
491     run_hook(ss->effects_hook, Xen_empty_list, S_effects_hook);
492 }
493 
494 
495 #define MARKS_ALLOC_SIZE 4
496 
add_mark(mus_long_t samp,const char * name,chan_info * cp)497 mark *add_mark(mus_long_t samp, const char *name, chan_info *cp)
498 {
499   int i, med;
500   mark **mps;
501   ed_list *ed;
502 
503   ed = cp->edits[cp->edit_ctr];
504   if (!(ed->marks))
505     {
506       ed->mark_size = MARKS_ALLOC_SIZE;
507       ed->mark_ctr = -1;
508       ed->marks = (mark **)calloc(MARKS_ALLOC_SIZE, sizeof(mark *));
509     }
510 
511   ed->mark_ctr++;
512   if (ed->mark_ctr >= ed->mark_size)
513     {
514       ed->mark_size += MARKS_ALLOC_SIZE;
515       ed->marks = (mark **)realloc(ed->marks, ed->mark_size * sizeof(mark *));
516       for (i = ed->mark_size - MARKS_ALLOC_SIZE; i < ed->mark_size; i++) ed->marks[i] = NULL;
517     }
518 
519   mps = ed->marks;
520   med = ed->mark_ctr;
521 
522   if (med == 0)
523     {
524       if (mps[0]) free_mark(mps[0]);
525       mps[0] = make_mark(samp, name);
526       run_mark_hook(cp, mps[0]->id, MARK_ADD);
527       return(mps[0]);
528     }
529   else
530     {
531       for (i = 0; i < med; i++) /* not <= because we pre-incremented above */
532 	{
533 	  mark *mp;
534 	  mp = mps[i];
535 	  if (samp < mp->samp)
536 	    {
537 	      int j;
538 	      if (mps[med]) free_mark(mps[med]);
539 	      for (j = med; j > i; j--)
540 		mps[j] = mps[j - 1];
541 	      mps[i] = make_mark(samp, name);
542 	      run_mark_hook(cp, mps[i]->id, MARK_ADD);
543 	      return(mps[i]);
544 	    }
545 	}
546       /* insert at end */
547       if (mps[med]) free_mark(mps[med]);
548       mps[med] = make_mark(samp, name);
549       run_mark_hook(cp, mps[med]->id, MARK_ADD);
550       return(mps[med]);
551     }
552 }
553 
554 
delete_mark_samp(mus_long_t samp,chan_info * cp)555 bool delete_mark_samp(mus_long_t samp, chan_info *cp)
556 {
557   if (cp)
558     {
559       ed_list *ed;
560       ed = cp->edits[cp->edit_ctr];
561       if (ed->marks)
562 	{
563 	  mark **mps;
564 	  mps = ed->marks;
565 	  if (mps)
566 	    {
567 	      int i, edm;
568 	      edm = ed->mark_ctr;
569 	      for (i = 0; i <= edm; i++)
570 		{
571 		  mark *mp;
572 		  mp = mps[i];
573 		  if (mp->samp == samp)
574 		    {
575 		      axis_info *ap;
576 		      int id = -1;
577 		      ap = cp->axis;
578 		      if ((mp->samp >= ap->losamp) && (mp->samp <= ap->hisamp)) erase_mark(cp, mp);
579 		      id = mp->id;
580 		      free_mark(mp);
581 		      mps[i] = NULL;
582 		      if (i < edm)
583 			{
584 			  int j;
585 			  for (j = i; j < edm; j++) mps[j] = mps[j + 1];
586 			  mps[edm] = NULL;
587 			}
588 		      ed->mark_ctr--;
589 		      run_mark_hook(cp, id, MARK_DELETE);
590 		      return(true);
591 		    }
592 		}
593 	    }
594 	}
595     }
596   return(false);
597 }
598 
599 
delete_mark_id(int id,chan_info * cp)600 static bool delete_mark_id(int id, chan_info *cp)
601 {
602   if (cp)
603     {
604       ed_list *ed;
605       ed = cp->edits[cp->edit_ctr];
606       if (ed->marks)
607 	{
608 	  mark **mps;
609 	  mps = ed->marks;
610 	  if (mps)
611 	    {
612 	      int i, edm;
613 	      edm = ed->mark_ctr;
614 	      for (i = 0; i <= edm; i++)
615 		{
616 		  mark *mp;
617 		  mp = mps[i];
618 		  if (mp->id == id)
619 		    {
620 		      axis_info *ap;
621 		      ap = cp->axis;
622 		      if ((mp->samp >= ap->losamp) && (mp->samp <= ap->hisamp)) erase_mark(cp, mp);
623 		      free_mark(mp);
624 		      mps[i] = NULL;
625 		      if (i < edm)
626 			{
627 			  int j;
628 			  for (j = i; j < edm; j++) mps[j] = mps[j + 1];
629 			  mps[edm] = NULL;
630 			}
631 		      ed->mark_ctr--;
632 		      run_mark_hook(cp, id, MARK_DELETE);
633 		      return(true);
634 		    }
635 		}
636 	    }
637 	}
638     }
639   return(false);
640 }
641 
642 
delete_marks(chan_info * cp)643 static void delete_marks(chan_info *cp)
644 {
645   if (cp)
646     {
647       ed_list *ed;
648       ed = cp->edits[cp->edit_ctr];
649       if (ed->marks)
650 	{
651 	  mark **mps;
652 	  mps = ed->marks;
653 	  if (mps)
654 	    {
655 	      int i;
656 	      for (i = 0; i < ed->mark_size; i++)
657 		{
658 		  mark *mp;
659 		  mp = mps[i];
660 		  if (mp)
661 		    {
662 		      axis_info *ap;
663 		      ap = cp->axis;
664 		      if ((mp->samp >= ap->losamp) && (mp->samp <= ap->hisamp)) erase_mark(cp, mp);
665 		      free_mark(mp);
666 		      mps[i] = NULL;
667 		    }
668 		}
669 	      ed->mark_ctr = -1;
670 	      run_mark_hook(cp, -1, MARKS_DELETE);
671 	    }
672 	}
673     }
674 }
675 
676 
free_mark_list(ed_list * ed)677 void free_mark_list(ed_list *ed)
678 {
679   if (ed->marks)
680     {
681       int j;
682       for (j = 0; j < ed->mark_size; j++)
683 	{
684 	  mark *mp;
685 	  mp = ed->marks[j];
686 	  if (mp) free_mark(mp);
687 	  ed->marks[j] = NULL;
688 	}
689       free(ed->marks);
690       ed->marks = NULL;
691       ed->mark_size = 0;
692     }
693 }
694 
695 
696 /* save and restore across update-sound */
697 
698 typedef struct {
699   mark **marks;
700   int ctr;
701   int size;
702 } mark_info;
703 
704 typedef struct {
705   mark_info **ms;
706   int size;
707 } marks_info;
708 
sound_store_marks(snd_info * sp)709 void *sound_store_marks(snd_info *sp)
710 {
711   uint32_t i;
712   mark_info **res;
713   marks_info *rtn;
714   res = (mark_info **)calloc(sp->nchans, sizeof(mark_info *));
715   rtn = (marks_info *)calloc(1, sizeof(marks_info));
716   rtn->ms = res;
717   rtn->size = sp->nchans;
718   for (i = 0; i < sp->nchans; i++)
719     {
720       chan_info *cp;
721       cp = sp->chans[i];
722       if (cp)
723 	{
724 	  ed_list *ed;
725 	  ed = cp->edits[cp->edit_ctr];
726 	  if (ed)
727 	    {
728 	      res[i] = (mark_info *)calloc(1, sizeof(mark_info));
729 	      res[i]->marks = ed->marks;
730 	      res[i]->ctr = ed->mark_ctr;
731 	      res[i]->size = ed->mark_size;
732 	      ed->marks = NULL;
733 	      ed->mark_size = 0;
734 	      ed->mark_ctr = -1;
735 	    }
736 	}
737     }
738   return((void *)rtn);
739 }
740 
741 
sound_restore_marks(snd_info * sp,void * mrk)742 void sound_restore_marks(snd_info *sp, void *mrk)
743 {
744   marks_info *mrks = (marks_info *)mrk;
745   if (mrks)
746     {
747       int i, lim;
748       mark_info **marks;
749       marks = mrks->ms;
750       lim = mrks->size;
751       if ((int)sp->nchans < lim) lim = sp->nchans; /* update can change channel number either way */
752       for (i = 0; i < lim; i++)
753 	{
754 	  if (marks[i])
755 	    {
756 	      ed_list *ed;
757 	      chan_info *cp;
758 	      cp = sp->chans[i];
759 	      ed = cp->edits[0];
760 	      if (ed)
761 		{
762 		  ed->marks = marks[i]->marks;
763 		  ed->mark_ctr = marks[i]->ctr;
764 		  ed->mark_size = marks[i]->size;
765 		}
766 	    }
767 	}
768       for (i = 0; i < mrks->size; i++)
769 	if (marks[i]) free(marks[i]);
770       /* possible memleak here if chan num has lessened */
771       free(marks);
772       free(mrks);
773     }
774 }
775 
776 
find_nth_mark(chan_info * cp,int count)777 static mark *find_nth_mark(chan_info *cp, int count)
778 {
779   int i, c;
780   mus_long_t samp;
781   mark *mp = NULL;
782   if ((!cp) || (!cp->edits[cp->edit_ctr]->marks)) return(NULL);
783   if (count > 0) c = count; else c = -count;
784   samp = cursor_sample(cp);
785   for (i = 0; i < c; i++)
786     {
787       if (count > 0) mp = find_next_mark(samp, cp);
788       else mp = find_previous_mark(samp, cp);
789       if (!mp) break;
790       samp = mp->samp;
791     }
792   return(mp);
793 }
794 
795 
goto_mark(chan_info * cp,int count)796 bool goto_mark(chan_info *cp, int count)
797 {
798   mark *mp;
799   if ((!cp) || (!cp->edits[cp->edit_ctr]->marks)) return(false);
800   mp = find_nth_mark(cp, count);
801   if (!mp) return(false);
802   cursor_moveto(cp, mp->samp);
803   return(true);
804 }
805 
806 
807 #if 0
808 static mark *find_named_mark_1(chan_info *cp, mark *mp, void *uname)
809 {
810   char *name = (char *)uname;
811   if ((mp->name) && (mus_strcmp(mp->name, name))) return(mp);
812   else return(NULL);
813 }
814 
815 
816 static mark *find_named_mark(chan_info *cp, const char *name)
817 {
818   return(map_over_marks(cp, find_named_mark_1, (void *)name, READ_FORWARD));
819 }
820 
821 
822 void goto_named_mark(chan_info *cp, const char *name)
823 {
824   mark *mp;
825   mp = find_named_mark(cp, name);
826   if (mp) cursor_moveto(cp, mp->samp);
827 }
828 #endif
829 
830 
active_mark_1(chan_info * cp,mark * mp,void * ignore)831 static mark *active_mark_1(chan_info *cp, mark *mp, void *ignore)
832 {
833   axis_info *ap;
834   ap = cp->axis;
835   if ((mp->samp >= ap->losamp) && (mp->samp <= ap->hisamp)) return(mp);
836   return(NULL);
837 }
838 
839 
active_mark(chan_info * cp)840 mark *active_mark(chan_info *cp)
841 {
842   return(map_over_marks(cp, active_mark_1, NULL, READ_FORWARD));
843 }
844 
845 
mark_beg(chan_info * cp)846 mus_long_t mark_beg(chan_info *cp)
847 {
848   /* called only in snd-chn.c for active zoom */
849   mark *mp;
850   mp = active_mark(cp);
851   if (mp) return(mp->samp);
852   return(-1);
853 }
854 
855 
856 typedef struct {
857   mus_long_t last_samp;
858 } dpy_last;
859 
display_channel_marks_1(chan_info * cp,mark * mp,void * m)860 static mark *display_channel_marks_1(chan_info *cp, mark *mp, void *m)
861 {
862   axis_info *ap;
863   dpy_last *ls = (dpy_last *)m;
864   ap = cp->axis;
865   if (mp->samp > ap->hisamp) return(mp); /* terminates loop */
866   if (mp->samp == ls->last_samp)
867     return(NULL);
868   /* actually this should notice how wide in samples the mark stem is and avoid any redraw until we get to the next clear pixel */
869   else ls->last_samp = mp->samp;         /* avoid drawing twice at same point == erase */
870   if ((mp->samp >= ap->losamp) &&
871       (mp->samp <= ap->hisamp) &&
872       (mp != moving_mark))
873     draw_mark(cp, mp);
874   return(NULL);
875 }
876 
877 
display_channel_marks(chan_info * cp)878 void display_channel_marks(chan_info *cp)
879 {
880   if ((cp->edits[cp->edit_ctr]->marks) && (cp->show_marks))
881     {
882       dpy_last ls;
883       ls.last_samp = -1;
884       map_over_marks(cp, display_channel_marks_1, (void *)(&ls), READ_FORWARD);
885     }
886 }
887 
888 
ripple_marks(chan_info * cp,mus_long_t beg,mus_long_t change)889 void ripple_marks(chan_info *cp, mus_long_t beg, mus_long_t change)
890 {
891   /* if change = 0, just set ptr, else copy and fixup with deletions */
892   /* this is called after the tree has been pushed forward, so edit_ctr is ahead of us */
893   /* but we don't do anything if no marks */
894 
895   if ((cp) && (cp->edit_ctr > 0))
896     {
897       ed_list *old_ed, *new_ed;
898       new_ed = cp->edits[cp->edit_ctr];
899       old_ed = cp->edits[cp->edit_ctr - 1];
900 
901       if (new_ed->marks)
902 	{
903 	  /* release current */
904 	  free_mark_list(new_ed);
905 	}
906 
907       /* copy old with position change */
908       new_ed->mark_ctr = old_ed->mark_ctr;
909       new_ed->mark_size = old_ed->mark_size;
910       if (new_ed->mark_size > 0)
911 	{
912 	  new_ed->marks = (mark **)calloc(new_ed->mark_size, sizeof(mark *));
913 	  if (new_ed->mark_ctr >= 0)
914 	    {
915 	      int i;
916 	      mark **mps, **mpo;
917 	      mark *mp;
918 	      mps = new_ed->marks;
919 	      mpo = old_ed->marks;
920 	      for (i = 0; i <= new_ed->mark_ctr; i++)
921 		mps[i] = copy_mark(mpo[i]);
922 	      if (change < 0)
923 		{
924 		  mus_long_t end;
925 		  /* if (change < 0) and any marks are between beg and beg+change, they must be deleted */
926 		  end = beg - change - 1;
927 		  i = 0;
928 		  while (i <= new_ed->mark_ctr)
929 		    {
930 		      mp = mps[i];
931 		      if ((mp->samp >= beg) && (mp->samp <= end)) /* was mp->samp > beg, ditto end, beg can = end */
932 			delete_mark_samp(mp->samp, cp);           /* changes cp->mark_ctr, hence the while loop?  */
933 		      else
934 			{
935 			  if (mp->samp > beg)                     /* don't change marks that precede the point of the change */
936 			    mp->samp += change;
937 			  i++;
938 			}
939 		    }
940 		}
941 	      else
942 		{
943 		  if (change > 0)
944 		    for (i = 0; i <= new_ed->mark_ctr; i++)
945 		      {
946 			mp = mps[i];
947 			if (mp->samp > beg) mp->samp += change;
948 		      }
949 		}
950 	    }
951 	}
952     }
953 }
954 
955 
mark_define_region(chan_info * cp,int count)956 bool mark_define_region(chan_info *cp, int count)
957 {
958   if ((cp) && (max_regions(ss) > 0))
959     {
960       ed_list *ed;
961       ed = cp->edits[cp->edit_ctr];
962       if (ed->marks)
963 	{
964 	  mus_long_t beg;
965 	  mark *mp;
966 	  beg = cursor_sample(cp);
967 	  mp = find_nth_mark(cp, count);
968 	  if (mp)
969 	    {
970 	      mus_long_t end;
971 	      end = mp->samp;
972 	      if (end != beg)
973 		{
974 		  mus_long_t ends[1];
975 		  sync_info *si;
976 		  int i;
977 		  ends[0] = end;
978 		  if (end < beg)
979 		    {
980 		      ends[0] = beg;
981 		      beg = end;
982 		    }
983 		  deactivate_selection();
984 		  si = sync_to_chan(cp);
985 		  si->begs[0] = beg;
986 		  define_region(si, ends);
987 		  for (i = 0; i < si->chans; i++)
988 		    {
989 		      reactivate_selection(si->cps[i], beg, ends[0]);
990 		      update_graph(si->cps[i]);
991 		    }
992 		  free_sync_info(si);
993 		  return(true);
994 		}
995 	    }
996 	}
997     }
998   return(false);
999 }
1000 
1001 
reverse_mark_1(chan_info * cp,mark * mp,void * um)1002 static mark *reverse_mark_1(chan_info *cp, mark *mp, void *um)
1003 {
1004   mark *m = (mark *)um;
1005   mp->samp = m->samp - mp->samp;
1006   return(NULL);
1007 }
1008 
1009 
reverse_marks(chan_info * cp,mus_long_t beg,mus_long_t dur)1010 void reverse_marks(chan_info *cp, mus_long_t beg, mus_long_t dur) /* beg -1 for full sound */
1011 {
1012   ed_list *ed;
1013   ed = cp->edits[cp->edit_ctr];
1014   if (ed->marks)
1015     {
1016       mark *m;
1017       mark **mps;
1018       mps = ed->marks;
1019       if (beg == -1)
1020 	{
1021 	  m = make_mark_1(current_samples(cp) - 1, NULL, 0, 0);
1022 	  map_over_marks(cp, reverse_mark_1, (void *)m, READ_FORWARD);
1023 	  free_mark(m);
1024 	}
1025       else
1026 	{
1027 	  mus_long_t end;
1028 	  int i, marks;
1029 	  end = beg + dur - 1;
1030 	  marks = ed->mark_ctr;
1031 	  for (i = 0; i <= marks; i++)
1032 	    {
1033 	      m = mps[i];
1034 	      if ((m->samp >= beg) && (m->samp <= end))
1035 		m->samp = end - (m->samp - beg);
1036 	    }
1037 	}
1038       if (ed->mark_ctr >= 0)
1039 	qsort((void *)mps, ed->mark_ctr + 1, sizeof(mark *), compare_mark_samps);
1040     }
1041 }
1042 
1043 
src_marks(chan_info * cp,mus_float_t ratio,mus_long_t old_samps,mus_long_t new_samps,mus_long_t beg,bool over_selection)1044 void src_marks(chan_info *cp, mus_float_t ratio, mus_long_t old_samps, mus_long_t new_samps, mus_long_t beg, bool over_selection)
1045 {
1046   ed_list *ed;
1047   ed = cp->edits[cp->edit_ctr];
1048   if ((ed->marks) && (ed->mark_ctr >= 0))
1049     {
1050       int i, marks;
1051       mark *m;
1052       mark **mps;
1053       mps = ed->marks;
1054       marks = ed->mark_ctr;
1055       if (!over_selection)
1056 	{
1057 	  for (i = 0; i <= marks; i++)
1058 	    {
1059 	      m = mps[i];
1060 	      if (ratio > 0.0)
1061 		m->samp = (mus_long_t)(m->samp / ratio);
1062 	      else m->samp = (mus_long_t)((old_samps - 1 - m->samp) / (-ratio)); /* ratio < 0 here */
1063 	    }
1064 	}
1065       else
1066 	{
1067 	  mus_long_t end;
1068 	  end = beg + old_samps - 1;
1069 	  for (i = 0; i <= marks; i++)
1070 	    {
1071 	      m = mps[i];
1072 	      if ((m->samp >= beg) && (m->samp <= end))
1073 		{
1074 		  if (ratio > 0.0)
1075 		    m->samp = beg + (mus_long_t)((m->samp - beg) / ratio);
1076 		  else m->samp = beg + (mus_long_t)((old_samps - 1 - (m->samp - beg)) / (-ratio));
1077 		}
1078 	      else
1079 		{
1080 		  if (m->samp > end)
1081 		    m->samp += (new_samps - old_samps);
1082 		}
1083 	    }
1084 	}
1085       if (ratio < 0.0) qsort((void *)mps, marks + 1, sizeof(mark *), compare_mark_samps);
1086     }
1087 }
1088 
1089 
reset_marks(chan_info * cp,int cur_marks,mus_long_t * samps,mus_long_t end,mus_long_t extension,bool over_selection)1090 void reset_marks(chan_info *cp, int cur_marks, mus_long_t *samps, mus_long_t end, mus_long_t extension, bool over_selection)
1091 {
1092   ed_list *ed;
1093   ed = cp->edits[cp->edit_ctr];
1094   if ((ed->marks) && (ed->mark_ctr >= 0))
1095     {
1096       int i, marks;
1097       mark *m;
1098       mark **mps;
1099       mps = ed->marks;
1100       marks = ed->mark_ctr;
1101       if (over_selection)
1102 	for (i = 0; i <= marks; i++)
1103 	  {
1104 	    m = mps[i];
1105 	    if (m->samp > end) m->samp += extension;
1106 	  }
1107       for (i = 0; (i <= marks) && (i < cur_marks); i++)
1108 	{
1109 	  m = mps[i];
1110 	  if (samps[i] >= 0) m->samp = samps[i];
1111 	}
1112       qsort((void *)mps, marks + 1, sizeof(mark *), compare_mark_samps);
1113     }
1114 }
1115 
1116 
ripple_trailing_marks(chan_info * cp,mus_long_t beg,mus_long_t old_len,mus_long_t new_len)1117 void ripple_trailing_marks(chan_info *cp, mus_long_t beg, mus_long_t old_len, mus_long_t new_len)
1118 {
1119   ed_list *ed;
1120   ed = cp->edits[cp->edit_ctr];
1121   if ((ed->marks) && (ed->mark_ctr >= 0))
1122     {
1123       int i, marks;
1124       mark **mps;
1125       ripple_marks(cp, 0, 0);
1126       mps = ed->marks;
1127       marks = ed->mark_ctr;
1128       for (i = 0; i <= marks; i++)
1129 	{
1130 	  mark *m;
1131 	  m = mps[i];
1132 	  if (m->samp > (beg + old_len)) m->samp += (new_len - old_len);
1133 	}
1134     }
1135 }
1136 
1137 
swap_marks(chan_info * cp0,chan_info * cp1)1138 void swap_marks(chan_info *cp0, chan_info *cp1)
1139 {
1140   ed_list *ed0, *ed1;
1141   ed0 = cp0->edits[cp0->edit_ctr];
1142   ed1 = cp1->edits[cp1->edit_ctr];
1143   if ((ed0->marks) || (ed1->marks))
1144     {
1145       mark **mps0 = NULL, **mps1 = NULL;
1146       int ctr0 = -1, ctr1 = -1;
1147       int size0 = 0, size1 = 0;
1148       if (ed0->marks)
1149 	{
1150 	  mps0 = ed0->marks;
1151 	  ctr0 = ed0->mark_ctr;
1152 	  size0 = ed0->mark_size;
1153 	}
1154       if (ed1->marks)
1155 	{
1156 	  mps1 = ed1->marks;
1157 	  ctr1 = ed1->mark_ctr;
1158 	  size1 = ed1->mark_size;
1159 	}
1160       ed0->marks = mps1;
1161       ed0->mark_ctr = ctr1;
1162       ed0->mark_size = size1;
1163       ed1->marks = mps0;
1164       ed1->mark_ctr = ctr0;
1165       ed1->mark_size = size0;
1166     }
1167 }
1168 
1169 
1170 /* -------------------------------- SYNCD AND DRAGGED MARKS -------------------------------- */
1171 
1172 typedef struct {
1173   mark **marks;
1174   chan_info **chans;
1175   int marks_size;
1176   int mark_ctr;
1177   int sync;
1178   mus_long_t *initial_samples;
1179 } syncdata;
1180 
make_syncdata(int sync)1181 static syncdata *make_syncdata(int sync)
1182 {
1183   syncdata *sd;
1184   sd = (syncdata *)calloc(1, sizeof(syncdata));
1185   sd->sync = sync;
1186   sd->mark_ctr = 0;
1187   sd->marks_size = 8;
1188   sd->marks = (mark **)calloc(sd->marks_size, sizeof(mark *));
1189   sd->chans = (chan_info **)calloc(sd->marks_size, sizeof(chan_info *));
1190   sd->initial_samples = (mus_long_t *)calloc(sd->marks_size, sizeof(mus_long_t));
1191   return(sd);
1192 }
1193 
1194 
add_syncd_mark(syncdata * sd,mark * mp,chan_info * cp)1195 static void add_syncd_mark(syncdata *sd, mark *mp, chan_info *cp)
1196 {
1197   sd->marks[sd->mark_ctr] = mp;
1198   sd->initial_samples[sd->mark_ctr] = mp->samp;
1199   sd->chans[sd->mark_ctr++] = cp;
1200   if (sd->mark_ctr == sd->marks_size)
1201     {
1202       int i;
1203       sd->marks = (mark **)realloc(sd->marks, sd->marks_size * 2 * sizeof(mark *));
1204       sd->chans = (chan_info **)realloc(sd->chans, sd->marks_size * 2 * sizeof(chan_info *));
1205       /* why was initial_samples missing? 2-May-02 */
1206       sd->initial_samples = (mus_long_t *)realloc(sd->initial_samples, sd->marks_size * 2 * sizeof(mus_long_t));
1207       for (i = sd->marks_size; i < sd->marks_size * 2; i++) {sd->marks[i] = NULL; sd->chans[i] = NULL;}
1208       sd->marks_size *= 2;
1209     }
1210 }
1211 
1212 
gather_local_syncd_marks(chan_info * cp,mark * mp,void * usd)1213 static mark *gather_local_syncd_marks(chan_info *cp, mark *mp, void *usd)
1214 {
1215   syncdata *sd = (syncdata *)usd;
1216   if (sd->sync == mp->sync)
1217     add_syncd_mark(sd, mp, cp);
1218   return(NULL);
1219 }
1220 
1221 
gather_chan_syncd_marks(chan_info * cp,void * sd)1222 static void gather_chan_syncd_marks(chan_info *cp, void *sd)
1223 {
1224   map_over_marks(cp, gather_local_syncd_marks, sd, READ_FORWARD);
1225 }
1226 
1227 
gather_syncd_marks(int sync)1228 static syncdata *gather_syncd_marks(int sync)
1229 {
1230   syncdata *sd;
1231   sd = make_syncdata(sync);
1232   for_each_normal_chan_with_void(gather_chan_syncd_marks, (void *)sd);
1233   return(sd);
1234 }
1235 
1236 
free_syncdata(syncdata * sd)1237 static syncdata *free_syncdata(syncdata *sd)
1238 {
1239   if (sd)
1240     {
1241       if (sd->marks) free(sd->marks);
1242       if (sd->initial_samples) free(sd->initial_samples);
1243       if (sd->chans) free(sd->chans);
1244       free(sd);
1245     }
1246   return(NULL);
1247 }
1248 
1249 
1250 static bool mark_control_clicked = false; /* C-click of mark -> drag data as mark is dragged */
1251 static mus_long_t mark_initial_sample = 0;
1252 static syncdata *mark_sd = NULL;
1253 
1254 typedef struct {
1255   widget_t graph;
1256   point_t *p0, *p1;
1257   int lastpj;
1258   color_t color;
1259 } mark_context;
1260 
1261 static mark_context **mark_movers = NULL;
1262 
make_mark_context(chan_info * cp)1263 static mark_context *make_mark_context(chan_info *cp)
1264 {
1265   mark_context *g;
1266   g = (mark_context *)calloc(1, sizeof(mark_context));
1267   g->graph = channel_graph(cp);
1268   g->color = ss->mark_color;
1269   return(g);
1270 }
1271 
1272 
free_mark_context(mark_context * ms)1273 static mark_context *free_mark_context(mark_context *ms)
1274 {
1275   if (ms->p0) {free(ms->p0); ms->p0 = NULL;}
1276   if (ms->p1) {free(ms->p1); ms->p1 = NULL;}
1277   free(ms);
1278   return(NULL);
1279 }
1280 
1281 
1282 static void mark_save_graph(mark_context *ms, int j);
1283 static void make_mark_graph(chan_info *cp, mus_long_t initial_sample, mus_long_t current_sample, int which);
1284 
initialize_md_context(int size,chan_info ** cps)1285 static void initialize_md_context(int size, chan_info **cps)
1286 {
1287   int i;
1288   mark_movers = (mark_context **)calloc(size, sizeof(mark_context *));
1289   for (i = 0; i < size; i++)
1290     {
1291       mark_context *ms;
1292       mark_movers[i] = make_mark_context(cps[i]);
1293       ms = mark_movers[i];
1294       ms->lastpj = make_dragged_marks_graph(cps[i]);
1295       mark_save_graph(ms, ms->lastpj);
1296     }
1297 }
1298 
1299 
finalize_md_context(int size)1300 static void finalize_md_context(int size)
1301 {
1302   if (mark_movers)
1303     {
1304       int i;
1305       for (i = 0; i < size; i++)
1306 	if (mark_movers[i])
1307 	  free_mark_context(mark_movers[i]);
1308       free(mark_movers);
1309       mark_movers = NULL;
1310     }
1311 }
1312 
1313 
set_mark_control(chan_info * cp,mark * mp,int key_state)1314 void set_mark_control(chan_info *cp, mark *mp, int key_state)
1315 {
1316   mark_control_clicked = (key_state & ControlMask);
1317 
1318   if (mark_control_clicked)
1319     {
1320       mark_initial_sample = mp->samp;
1321       if (mark_sd)
1322 	{
1323 	  if ((mark_sd->mark_ctr > 1) &&
1324 	      (mark_sd->marks[0] != mp))
1325 	    {
1326 	      int loc;
1327 	      for (loc = 1; loc < mark_sd->mark_ctr; loc++)
1328 		if (mark_sd->marks[loc] == mp) break;
1329 	      if (loc < mark_sd->mark_ctr)
1330 		{
1331 		  chan_info *tc;
1332 		  mus_long_t ts;
1333 		  mark *tm;
1334 		  tm = mark_sd->marks[0];
1335 		  ts = mark_sd->initial_samples[0];
1336 		  tc = mark_sd->chans[0];
1337 		  mark_sd->marks[0] = mark_sd->marks[loc];
1338 		  mark_sd->initial_samples[0] = mark_sd->initial_samples[loc];
1339 		  mark_sd->chans[0] = mark_sd->chans[loc];
1340 		  mark_sd->marks[loc] = tm;
1341 		  mark_sd->initial_samples[loc] = ts;
1342 		  mark_sd->chans[loc] = tc;
1343 		}
1344 	    }
1345 	  initialize_md_context(mark_sd->mark_ctr, mark_sd->chans);
1346 	}
1347       else initialize_md_context(1, &cp);
1348     }
1349 }
1350 
1351 
hit_mark(chan_info * cp,int x,int y)1352 mark *hit_mark(chan_info *cp, int x, int y)
1353 {
1354   ed_list *ed;
1355   ed = cp->edits[cp->edit_ctr];
1356   if ((ed->marks) && (ed->mark_ctr >= 0))
1357     {
1358       axis_info *ap;
1359       ap = cp->axis;
1360 
1361       /* first check that we're in the top portion of the graph where the mark tabs are */
1362       if ((y >= ap->y_axis_y1) &&
1363 	  (y <= (ap->y_axis_y1 + mark_tag_height(ss) + 10)))               /*  + 10 for named marks -- checked again later */
1364 	{
1365 	  mark *mp;
1366 	  mdata *md;
1367 
1368 	  md = (mdata *)calloc(1, sizeof(mdata));
1369 	  md->x = x;
1370 	  md->y = y;
1371 	  md->all_done = (mark *)1;
1372 	  mp = map_over_marks(cp, hit_mark_1, (void *)md, READ_FORWARD);
1373 	  if (mp == (mark *)1) mp = NULL;
1374 	  free(md);
1375 
1376 	  if (mp)
1377 	    {
1378 	      if (mp->sync != 0)
1379 		{
1380 		  if (mark_sd) mark_sd = free_syncdata(mark_sd);
1381 		  mark_sd = gather_syncd_marks(mp->sync);
1382 		}
1383 	    }
1384 	  return(mp);
1385 	}
1386     }
1387   return(NULL);
1388 }
1389 
1390 
1391 #if (!USE_NO_GUI)
1392 
allocate_erase_grf_points(mark_context * ms)1393 static void allocate_erase_grf_points(mark_context *ms)
1394 {
1395   if (!ms->p0)
1396     {
1397       ms->p0 = (point_t *)calloc(POINT_BUFFER_SIZE, sizeof(point_t));
1398       ms->p1 = (point_t *)calloc(POINT_BUFFER_SIZE, sizeof(point_t));
1399     }
1400 }
1401 
1402 
backup_erase_grf_points(mark_context * ms,int nj)1403 static void backup_erase_grf_points(mark_context *ms, int nj)
1404 {
1405   ms->lastpj = nj;
1406   memcpy((void *)(ms->p0), (void *)get_grf_points(), nj * sizeof(point_t));
1407   memcpy((void *)(ms->p1), (void *)get_grf_points1(), nj * sizeof(point_t));
1408 }
1409 
1410 
mark_save_graph(mark_context * ms,int j)1411 static void mark_save_graph(mark_context *ms, int j)
1412 {
1413   allocate_erase_grf_points(ms);
1414   backup_erase_grf_points(ms, j);
1415 }
1416 
1417 
erase_and_draw_grf_points(mark_context * ms,chan_info * cp,int nj)1418 static void erase_and_draw_grf_points(mark_context *ms, chan_info *cp, int nj)
1419 {
1420   graphics_context *ax;
1421   point_t *points;
1422   chan_info *draw_cp;
1423 #if USE_MOTIF
1424   GC draw_gc, undraw_gc;
1425 #endif
1426 
1427   points = get_grf_points();
1428   draw_cp = channel_to_chan(cp);
1429   ax = draw_cp->ax;
1430 
1431   undraw_gc = erase_GC(draw_cp);
1432   draw_gc = copy_GC(draw_cp);
1433   if (draw_cp->time_graph_style == GRAPH_LINES)
1434     {
1435       ax->gc = undraw_gc;
1436       draw_lines(ax, ms->p0, ms->lastpj);
1437       ax->gc = draw_gc;
1438       draw_lines(ax, points, nj);
1439     }
1440   else
1441     {
1442       ax->gc = undraw_gc;
1443       draw_points(ax, ms->p0, ms->lastpj, draw_cp->dot_size);
1444       ax->gc = draw_gc;
1445       draw_points(ax, points, nj, draw_cp->dot_size);
1446     }
1447   backup_erase_grf_points(ms, nj);
1448   ax->gc = draw_gc;
1449 }
1450 
1451 
erase_and_draw_both_grf_points(mark_context * ms,chan_info * cp,int nj)1452 static void erase_and_draw_both_grf_points(mark_context *ms, chan_info *cp, int nj)
1453 {
1454   graphics_context *ax;
1455   point_t *points, *points1;
1456   chan_info *draw_cp;
1457 #if USE_MOTIF
1458   GC draw_gc, undraw_gc;
1459 #endif
1460 
1461   points = get_grf_points();
1462   points1 = get_grf_points1();
1463 
1464   draw_cp = channel_to_chan(cp);
1465   ax = draw_cp->ax;
1466 
1467   undraw_gc = erase_GC(draw_cp);
1468   draw_gc = copy_GC(draw_cp);
1469   if (draw_cp->time_graph_style == GRAPH_LINES)
1470     {
1471       ax->gc = undraw_gc;
1472       draw_lines(ax, ms->p0, ms->lastpj);
1473       draw_lines(ax, ms->p1, ms->lastpj);
1474       ax->gc = draw_gc;
1475       draw_lines(ax, points, nj);
1476       draw_lines(ax, points1, nj);
1477     }
1478   else
1479     {
1480       ax->gc = undraw_gc;
1481       draw_points(ax, ms->p0, ms->lastpj, draw_cp->dot_size);
1482       draw_points(ax, ms->p1, ms->lastpj, draw_cp->dot_size);
1483       ax->gc = draw_gc;
1484       draw_points(ax, points, nj, draw_cp->dot_size);
1485       draw_points(ax, points1, nj, draw_cp->dot_size);
1486     }
1487   backup_erase_grf_points(ms, nj);
1488   ax->gc = draw_gc;
1489 }
1490 #else
mark_save_graph(mark_context * ms,int j)1491 static void mark_save_graph(mark_context *ms, int j) {}
1492 #endif
1493 
1494 
move_syncd_mark(chan_info * cp,mark * m,int x)1495 static bool move_syncd_mark(chan_info *cp, mark *m, int x)
1496 {
1497   mus_long_t old_samp, diff;
1498   bool redraw;
1499   old_samp = m->samp;
1500   redraw = move_mark_1(cp, m, x);
1501   diff = m->samp - old_samp;
1502   if (diff != 0)
1503     {
1504       if ((mark_sd) && (mark_sd->mark_ctr > 1))
1505 	{
1506 	  int i;
1507 	  for (i = 0; i < mark_sd->mark_ctr; i++)
1508 	    {
1509 	      mark *mp;
1510 	      mp = mark_sd->marks[i];
1511 	      if (mp != m)
1512 		{
1513 		  axis_info *ap;
1514 		  mus_long_t samps;
1515 		  chan_info *ncp;
1516 		  ncp = mark_sd->chans[i];
1517 		  ap = ncp->axis;
1518 		  if ((mp->samp >= ap->losamp) &&
1519 		      (mp->samp <= ap->hisamp))
1520 		    erase_mark(ncp, mp);
1521 		  mp->samp += diff;
1522 		  if (mp->samp < 0) mp->samp = 0;
1523 		  samps = current_samples(ncp);
1524 		  if (mp->samp >= samps) mp->samp = samps - 1;
1525 		  if (mark_control_clicked)
1526 		    make_mark_graph(ncp, mark_sd->initial_samples[i], mp->samp, i);
1527 		  if ((mp->samp >= ap->losamp) &&
1528 		      (mp->samp <= ap->hisamp))
1529 		    draw_mark(ncp, mp);
1530 		}
1531 	    }
1532 	}
1533     }
1534   return(redraw);
1535 }
1536 
1537 
1538 #if (!USE_NO_GUI)
move_axis_to_track_mark(chan_info * cp)1539 static void move_axis_to_track_mark(chan_info *cp)
1540 {
1541   if (moving_mark)
1542     {
1543       bool redraw;
1544       if (moving_mark->sync)
1545 	redraw = move_syncd_mark(cp, moving_mark, last_mouse_x);
1546       else redraw = move_mark_1(cp, moving_mark, last_mouse_x);
1547       if (redraw) draw_mark(cp, moving_mark);
1548     }
1549 }
1550 #endif
1551 
1552 
move_mark(chan_info * cp,mark * mp,int x)1553 void move_mark(chan_info *cp, mark *mp, int x) /* from mouse drag callback in snd-chn.c, called whenever mark is visible */
1554 {
1555   bool redraw;
1556   last_mouse_x = x;
1557 
1558   if (mp->sync)
1559     redraw = move_syncd_mark(cp, mp, x);
1560   else redraw = move_mark_1(cp, mp, x);
1561 
1562   if (mark_control_clicked)
1563     make_mark_graph(cp, mark_initial_sample, mp->samp, 0);
1564 
1565   if (redraw) draw_mark(cp, mp);
1566 }
1567 
1568 
edit_dragged_mark(chan_info * cp,mark * m,mus_long_t initial_sample)1569 static void edit_dragged_mark(chan_info *cp, mark *m, mus_long_t initial_sample)
1570 {
1571   /* edit -- initial_sample is where we were when the drag started, ended at m->samp */
1572   mus_long_t num, mark_final_sample;
1573   int id;
1574   mark *new_m;
1575 
1576   mark_final_sample = m->samp;
1577   num = mark_final_sample - initial_sample;
1578   m->samp = initial_sample;
1579   id = m->id;
1580 
1581   if (num > 0)
1582     extend_with_zeros(cp, initial_sample, num, cp->edit_ctr, "drag mark");
1583       /* at this point, old mark pointer is irrelevant (it lives in the previous edit history list) */
1584       /*   but since the ripple didn't touch it, we need to move it forward to reflect the insertion */
1585   else
1586     if (num < 0)
1587       {
1588 	new_m = map_over_marks_with_int(cp, find_mark_id_1, id, READ_FORWARD);
1589 	new_m->samp = initial_sample;
1590 	delete_samples(mark_final_sample, -num, cp, cp->edit_ctr);
1591       }
1592 
1593   if (num != 0)
1594     {
1595       new_m = map_over_marks_with_int(cp, find_mark_id_1, id, READ_FORWARD);
1596       new_m->samp = mark_final_sample;
1597       update_graph(cp);
1598     }
1599 }
1600 
1601 
finish_moving_mark(chan_info * cp,mark * m)1602 void finish_moving_mark(chan_info *cp, mark *m) /* button release called from snd-chn.c */
1603 {
1604   if (watching_mouse) cancel_mark_watch(cp);
1605   if ((m->sync != 0) && (mark_sd))
1606     {
1607       int i;
1608       if (Xen_hook_has_list(mark_hook))
1609 	for (i = 0; i < mark_sd->mark_ctr; i++)
1610 	  run_mark_hook(mark_sd->chans[i], mark_sd->marks[i]->id, MARK_RELEASE);
1611       if (mark_control_clicked)
1612 	{
1613 	  for (i = mark_sd->mark_ctr - 1; i >= 0; i--)
1614 	    {
1615 	      /* do the edits in reverse order on the assumption that marks sharing a channel were ordered to begin with,
1616 	       *   so they'll happen in reverse order here, so the lower sample edits in rippling won't affect the higher
1617 	       */
1618 	      mark *sdm;
1619 	      sdm = mark_sd->marks[i];
1620 	      edit_dragged_mark(mark_sd->chans[i], sdm, mark_sd->initial_samples[i]);
1621 	    }
1622 	  finalize_md_context(mark_sd->mark_ctr);
1623 	}
1624       for (i = 0; i < mark_sd->mark_ctr; i++)
1625 	if (mark_sd->chans[i])
1626 	  {
1627 	    int j;
1628 	    sort_marks(mark_sd->chans[i]); /* resort marks in case movement scrambled them */
1629 	    for (j = i + 1; j < mark_sd->mark_ctr; j++)    /* only sort each channel once */
1630 	      if (mark_sd->chans[j] == mark_sd->chans[i])
1631 		mark_sd->chans[j] = NULL;
1632 	  }
1633     }
1634   else
1635     {
1636       run_mark_hook(cp, m->id, MARK_RELEASE);
1637       if (mark_control_clicked)
1638 	{
1639 	  edit_dragged_mark(cp, m, mark_initial_sample);
1640 	  finalize_md_context(1);
1641 	}
1642       sort_marks(cp);
1643     }
1644   if (mark_sd) mark_sd = free_syncdata(mark_sd);
1645 }
1646 
1647 
play_syncd_mark(chan_info * cp,mark * m)1648 void play_syncd_mark(chan_info *cp, mark *m)
1649 {
1650   syncdata *sd;
1651   sd = gather_syncd_marks(m->sync);
1652   if ((sd) && (sd->mark_ctr > 0))
1653     play_channels(sd->chans, sd->mark_ctr, sd->initial_samples, NULL, IN_BACKGROUND,
1654 		  C_int_to_Xen_integer(AT_CURRENT_EDIT_POSITION), false, "play sync'd mark", 0);
1655   if (sd) free_syncdata(sd);
1656 }
1657 
1658 
make_mark_graph(chan_info * cp,mus_long_t initial_sample,mus_long_t current_sample,int which)1659 static void make_mark_graph(chan_info *cp, mus_long_t initial_sample, mus_long_t current_sample, int which)
1660 {
1661 #if (!USE_NO_GUI)
1662   snd_info *sp;
1663   int j = 0;
1664   mus_long_t i, k, samps;
1665   axis_info *ap;
1666   double samples_per_pixel, x;
1667   int pixels;
1668   snd_fd *sf = NULL;
1669   int x_start, x_end;
1670   double start_time = 0.0, cur_srate;
1671 
1672   sp = cp->sound;
1673   ap = cp->axis;
1674   cur_srate = (double)snd_srate(sp);
1675   ap->losamp = (mus_long_t)(ap->x0 * cur_srate);
1676   if (ap->losamp < 0) ap->losamp = 0;
1677   if (ap->x0 != ((double)(ap->losamp) / cur_srate)) ap->losamp++;
1678   start_time = (double)(ap->losamp) / cur_srate;
1679   ap->hisamp = (mus_long_t)(ap->x1 * cur_srate);
1680   if ((ap->losamp == 0) && (ap->hisamp == 0)) return;
1681   x_start = ap->x_axis_x0;
1682   x_end = ap->x_axis_x1;
1683   samps = ap->hisamp - ap->losamp + 1;
1684   if ((x_start == x_end) && (samps > 10)) return; /* must be too-tiny graph */
1685   pixels = x_end - x_start;
1686   if (pixels >= POINT_BUFFER_SIZE) pixels = POINT_BUFFER_SIZE - 1;
1687   if ((x_start == x_end) || (samps <= 1))
1688     samples_per_pixel = 0.01; /* any non-zero value < 1.0 should be ok here */
1689   else samples_per_pixel = (mus_float_t)((double)(samps - 1) / (mus_float_t)pixels);
1690 
1691   /* this is assuming one dragged mark per channel */
1692   if ((samples_per_pixel < 5.0) &&
1693       (samps < POINT_BUFFER_SIZE))
1694     {
1695       double incr;
1696       sf = init_sample_read(ap->losamp, cp, READ_FORWARD);
1697       if (!sf) return;
1698       incr = (double)1.0 / cur_srate;
1699       if (current_sample < initial_sample)
1700 	{
1701 	  for (j = 0, i = ap->losamp, x = start_time; i <= ap->hisamp; i++, j++, x += incr)
1702 	    {
1703 	      if (i == current_sample)
1704 		for (k = current_sample; k < initial_sample; k++)
1705 		  read_sample(sf);
1706 	      set_grf_point(grf_x(x, ap), j, grf_y(read_sample(sf), ap));
1707 	    }
1708 	}
1709       else
1710 	{
1711 	  for (j = 0, i = ap->losamp, x = start_time; i <= ap->hisamp; i++, j++, x += incr)
1712 	    {
1713 	      double samp;
1714 	      if ((i < initial_sample) || (i >= current_sample))
1715 		samp = read_sample(sf);
1716 	      else samp = 0.0;
1717 	      set_grf_point(grf_x(x, ap), j, grf_y(samp, ap));
1718 	    }
1719 	}
1720       erase_and_draw_grf_points(mark_movers[which], cp, j);
1721     }
1722   else
1723     {
1724       mus_float_t ymin, ymax;
1725       int xi;
1726       double xf;
1727       if (peak_env_usable(cp, samples_per_pixel, ap->hisamp, false, cp->edit_ctr, (samps > PEAK_ENV_CUTOFF)))
1728 	{
1729 	  /* needs two sets of pointers and a frame within the amp env:
1730 	   *   sample given mark edit: i and xk
1731 	   *   sample within (original, unedited) amp env: ii and xki (xf)
1732 	   *   frame bounds within amp env if relevant: k and kk
1733 	   * this is confusing code!
1734 	   */
1735 	  double step, xk, xki;
1736 	  mus_long_t ii;
1737 	  peak_env_info *ep;
1738 	  ep = cp->edits[cp->edit_ctr]->peak_env;
1739 	  step = samples_per_pixel / (mus_float_t)(ep->samps_per_bin);
1740 	  xf = (double)(ap->losamp) / (double)(ep->samps_per_bin);
1741 	  j = 0;
1742 	  x = ap->x0;
1743 	  xi = grf_x(x, ap);
1744 	  i = ap->losamp;
1745 	  ii = ap->losamp;
1746 	  xk = i;
1747 	  xki = (double)(ap->losamp);
1748 	  while (i <= ap->hisamp)
1749 	    {
1750 	      mus_long_t kk;
1751 	      k = (mus_long_t)xf;
1752 	      kk = (mus_long_t)(xf + step);
1753 	      if (((current_sample >= initial_sample) &&
1754 		   (i >= initial_sample) &&
1755 		   (i < current_sample)) ||
1756 		  (kk >= ep->peak_env_size))
1757 		{
1758 		  ymin = 0.0;
1759 		  ymax = 0.0;
1760 		}
1761 	      else
1762 		{
1763 		  ymin = ep->fmax;
1764 		  ymax = ep->fmin;
1765 		  for (; k <= kk; k++)
1766 		    {
1767 		      if (ep->data_min[k] < ymin) ymin = ep->data_min[k];
1768 		      if (ep->data_max[k] > ymax) ymax = ep->data_max[k];
1769 		    }
1770 		}
1771 	      set_grf_points(xi++, j++,
1772 			     grf_y(ymin, ap),
1773 			     grf_y(ymax, ap));
1774 	      xk += samples_per_pixel;
1775 	      i = (mus_long_t)xk;
1776 	      if ((current_sample < initial_sample) &&
1777 		  (ii >= current_sample) &&
1778 		  (ii < initial_sample))
1779 		{
1780 		  xf = (mus_float_t)((double)initial_sample / (mus_float_t)ep->samps_per_bin);
1781 		  ii = initial_sample;
1782 		  xki = (double)initial_sample;
1783 		}
1784 	      else
1785 		{
1786 		  if ((current_sample < initial_sample) ||
1787 		      (i >= current_sample) ||
1788 		      (i < initial_sample))
1789 		    {
1790 		      xf += step;
1791 		      xki += samples_per_pixel;
1792 		      ii = (mus_long_t)xki;
1793 		    }
1794 		}
1795 	    }
1796 	  erase_and_draw_both_grf_points(mark_movers[which], cp, j);
1797 	}
1798       else
1799 	{
1800 	  mus_float_t msamp;
1801 	  sf = init_sample_read(ap->losamp, cp, READ_FORWARD);
1802 	  if (!sf) return;
1803 	  j = 0;      /* graph point counter */
1804 	  x = ap->x0;
1805 	  xi = grf_x(x, ap);
1806 	  xf = 0.0;     /* samples per pixel counter */
1807 	  ymin = MIN_INIT;
1808 	  ymax = MAX_INIT;
1809 	  if (current_sample < initial_sample)
1810 	    {
1811 	      for (i = ap->losamp; i <= ap->hisamp; i++)
1812 		{
1813 		  if (i == current_sample)
1814 		    for (k = current_sample; k < initial_sample; k++)
1815 		      read_sample(sf);
1816 		  msamp = read_sample(sf);
1817 		  if (msamp > ymax) ymax = msamp;
1818 		  if (msamp < ymin) ymin = msamp;
1819 		  xf += 1.0;
1820 		  if (xf > samples_per_pixel)
1821 		    {
1822 		      set_grf_points(xi, j,
1823 				     grf_y(ymin, ap),
1824 				     grf_y(ymax, ap));
1825 		      xi++;
1826 		      j++;
1827 		      xf -= samples_per_pixel;
1828 		      ymin = MIN_INIT;
1829 		      ymax = MAX_INIT;
1830 		    }
1831 		}
1832 	    }
1833 	  else
1834 	    {
1835 	      for (i = ap->losamp, xf = 0.0; i <= ap->hisamp; i++)
1836 		{
1837 		  if ((i < initial_sample) || (i >= current_sample))
1838 		    msamp = read_sample(sf);
1839 		  else msamp = 0.0;
1840 		  if (msamp > ymax) ymax = msamp;
1841 		  if (msamp < ymin) ymin = msamp;
1842 		  xf += 1.0;
1843 		  if (xf > samples_per_pixel)
1844 		    {
1845 		      set_grf_points(xi, j,
1846 				     grf_y(ymin, ap),
1847 				     grf_y(ymax, ap));
1848 		      xi++;
1849 		      j++;
1850 		      xf -= samples_per_pixel;
1851 		      ymin = MIN_INIT;
1852 		      ymax = MAX_INIT;
1853 		    }
1854 		}
1855 	    }
1856 	  erase_and_draw_both_grf_points(mark_movers[which], cp, j);
1857 	}
1858     }
1859   free_snd_fd(sf);
1860 #endif
1861 }
1862 
1863 
1864 #if (!USE_NO_GUI)
1865 
1866 /* -------------------------------- display mark -------------------------------- */
1867 
show_mark(chan_info * cp,mark * mp,bool show)1868 static void show_mark(chan_info *cp, mark *mp, bool show)
1869 {
1870   int top, cx, y0, y1;
1871   axis_info *ap;
1872   graphics_context *ax;
1873 
1874 #if USE_MOTIF
1875   #define STRING_Y_OFFSET 6
1876 #endif
1877 
1878   ap = cp->axis;
1879   top = ap->y_axis_y1;
1880   y1 = top;
1881   y0 = ap->y_axis_y0;
1882   if (mp->name) top += 10;
1883   cx = grf_x((double)(mp->samp) / (double)snd_srate(cp->sound), ap);
1884 
1885   ax = mark_tag_context(cp);
1886 
1887   if (mp->name)
1888     {
1889       int len;
1890 #if USE_MOTIF
1891       ax->current_font = ss->peaks_fontstruct->fid;
1892       XSetFont(ax->dp, ax->gc, ss->peaks_fontstruct->fid);
1893 #endif
1894       len = mark_name_width(mp->name);
1895       draw_string(ax, (int)(cx - 0.5 * len), y1 + STRING_Y_OFFSET, mp->name, strlen(mp->name));
1896     }
1897 
1898   fill_rectangle(ax,
1899 		 cx - mark_tag_width(ss), top,
1900 		 2 * mark_tag_width(ss), mark_tag_height(ss));
1901   draw_line(ax, cx, top + 4, cx, y0);
1902 
1903   if (mp->samp != cursor_sample(cp))
1904     fill_polygon(ax, 4,
1905 		 cx, y0,
1906 		 cx + play_arrow_size(ss), y0 + play_arrow_size(ss),
1907 		 cx, y0 + 2 * play_arrow_size(ss),
1908 		 cx, y0);
1909   mp->visible = show;
1910 }
1911 
1912 #else
1913 /* no gui */
show_mark(chan_info * cp,mark * mp,bool show)1914 static void show_mark(chan_info *cp, mark *mp, bool show) {}
1915 #endif
1916 
1917 
1918 
1919 /* ---------------------------------------- mark object ---------------------------------------- */
1920 
1921 typedef struct {
1922   int n;
1923 } xen_mark;
1924 
1925 
1926 #define Xen_to_xen_mark(arg) ((xen_mark *)Xen_object_ref(arg))
1927 
xen_mark_to_int(Xen n)1928 int xen_mark_to_int(Xen n)
1929 {
1930   xen_mark *mx;
1931   mx = Xen_to_xen_mark(n);
1932   return(mx->n);
1933 }
1934 
1935 
1936 static Xen_object_type_t xen_mark_tag;
1937 
xen_is_mark(Xen obj)1938 bool xen_is_mark(Xen obj)
1939 {
1940   return(Xen_c_object_is_type(obj, xen_mark_tag));
1941 }
1942 
1943 #if (!HAVE_SCHEME)
xen_mark_free(xen_mark * v)1944 static void xen_mark_free(xen_mark *v) {if (v) free(v);}
1945 
Xen_wrap_free(xen_mark,free_xen_mark,xen_mark_free)1946 Xen_wrap_free(xen_mark, free_xen_mark, xen_mark_free)
1947 #else
1948 static s7_pointer s7_xen_mark_free(s7_scheme *sc, s7_pointer obj)
1949 {
1950   xen_mark *v;
1951   v = (xen_mark *)s7_c_object_value(obj);
1952   if (v) free(v);
1953   return(NULL);
1954 }
1955 #endif
1956 
1957 static char *xen_mark_to_string(xen_mark *v)
1958 {
1959   #define MARK_PRINT_BUFFER_SIZE 64
1960   char *buf;
1961   if (!v) return(NULL);
1962   buf = (char *)calloc(MARK_PRINT_BUFFER_SIZE, sizeof(char));
1963   snprintf(buf, MARK_PRINT_BUFFER_SIZE, "#<mark %d>", v->n);
1964   return(buf);
1965 }
1966 
1967 
1968 #if HAVE_FORTH || HAVE_RUBY
Xen_wrap_print(xen_mark,print_xen_mark,xen_mark_to_string)1969 Xen_wrap_print(xen_mark, print_xen_mark, xen_mark_to_string)
1970 
1971 static Xen g_xen_mark_to_string(Xen obj)
1972 {
1973   char *vstr;
1974   Xen result;
1975   #define S_xen_mark_to_string "mark->string"
1976   Xen_check_type(xen_is_mark(obj), obj, 1, S_xen_mark_to_string, "a mark");
1977   vstr = xen_mark_to_string(Xen_to_xen_mark(obj));
1978   result = C_string_to_Xen_string(vstr);
1979   free(vstr);
1980   return(result);
1981 }
1982 #else
1983 #if HAVE_SCHEME
g_xen_mark_to_string(s7_scheme * sc,s7_pointer args)1984 static s7_pointer g_xen_mark_to_string(s7_scheme *sc, s7_pointer args)
1985 {
1986   char *vstr;
1987   s7_pointer result;
1988   vstr = xen_mark_to_string(Xen_to_xen_mark(s7_car(args)));
1989   result = C_string_to_Xen_string(vstr);
1990   free(vstr);
1991   return(result);
1992 }
1993 #endif
1994 #endif
1995 
1996 
1997 #if (!HAVE_SCHEME)
xen_mark_equalp(xen_mark * v1,xen_mark * v2)1998 static bool xen_mark_equalp(xen_mark *v1, xen_mark *v2)
1999 {
2000   return((v1 == v2) ||
2001 	 (v1->n == v2->n));
2002 }
2003 
equalp_xen_mark(Xen obj1,Xen obj2)2004 static Xen equalp_xen_mark(Xen obj1, Xen obj2)
2005 {
2006   if ((!(xen_is_mark(obj1))) || (!(xen_is_mark(obj2)))) return(Xen_false);
2007   return(C_bool_to_Xen_boolean(xen_mark_equalp(Xen_to_xen_mark(obj1), Xen_to_xen_mark(obj2))));
2008 }
2009 #endif
2010 
2011 
xen_mark_make(int n)2012 static xen_mark *xen_mark_make(int n)
2013 {
2014   xen_mark *new_v;
2015   new_v = (xen_mark *)malloc(sizeof(xen_mark));
2016   new_v->n = n;
2017   return(new_v);
2018 }
2019 
2020 #if HAVE_SCHEME
2021   static s7_pointer g_mark_methods = NULL;
2022 #endif
2023 
new_xen_mark(int n)2024 Xen new_xen_mark(int n)
2025 {
2026   xen_mark *mx;
2027   if (n < 0)
2028     return(Xen_false);
2029 
2030   mx = xen_mark_make(n);
2031 #if HAVE_SCHEME
2032   {
2033     s7_pointer m;
2034     m = Xen_make_object(xen_mark_tag, mx, 0, free_xen_mark); /* last 2 args ignored */
2035     s7_c_object_set_let(s7, m, g_mark_methods);
2036     return(m);
2037   }
2038 #else
2039   return(Xen_make_object(xen_mark_tag, mx, 0, free_xen_mark));
2040 #endif
2041 }
2042 
2043 
2044 #if HAVE_SCHEME
s7_xen_mark_is_equal(s7_scheme * sc,s7_pointer args)2045 static s7_pointer s7_xen_mark_is_equal(s7_scheme *sc, s7_pointer args)
2046 {
2047   s7_pointer p1, p2;
2048   p1 = s7_car(args);
2049   p2 = s7_cadr(args);
2050   if (p1 == p2) return(s7_t(sc));
2051   if (s7_c_object_type(p2) == xen_mark_tag)
2052     return(s7_make_boolean(sc, (((xen_mark *)s7_c_object_value(p1))->n == ((xen_mark *)s7_c_object_value(p2))->n)));
2053   return(s7_f(sc));
2054 }
2055 
s7_xen_mark_copy(s7_scheme * sc,s7_pointer args)2056 static Xen s7_xen_mark_copy(s7_scheme *sc, s7_pointer args)
2057 {
2058   s7_pointer obj;
2059   int id;
2060   mark *m;
2061   chan_info *cps[1] = {NULL};
2062   obj = s7_car(args);
2063   id = xen_mark_to_int(obj);
2064   m = find_mark_from_id(id, cps, AT_CURRENT_EDIT_POSITION);
2065   if (m)
2066     {
2067       mark *new_m;
2068       new_m = add_mark(m->samp, m->name, cps[0]);
2069       new_m->sync = m->sync;
2070       return(new_xen_mark(new_m->id));
2071     }
2072   return(obj);
2073 }
2074 
2075 static s7_pointer mark_to_let_func = NULL;
2076 #endif
2077 
2078 
init_xen_mark(void)2079 static void init_xen_mark(void)
2080 {
2081 #if HAVE_SCHEME
2082   g_mark_methods = s7_openlet(s7, s7_inlet(s7, s7_list(s7, 2, s7_make_symbol(s7, "object->let"), mark_to_let_func)));
2083   s7_gc_protect(s7, g_mark_methods);
2084   xen_mark_tag = s7_make_c_type(s7, "<mark>");
2085   s7_c_type_set_gc_free(s7, xen_mark_tag, s7_xen_mark_free);
2086   s7_c_type_set_is_equal(s7, xen_mark_tag, s7_xen_mark_is_equal);
2087   s7_c_type_set_copy(s7, xen_mark_tag, s7_xen_mark_copy);
2088   s7_c_type_set_to_string(s7, xen_mark_tag, g_xen_mark_to_string);
2089 #else
2090 #if HAVE_RUBY
2091   xen_mark_tag = Xen_make_object_type("XenMark", sizeof(xen_mark));
2092 #else
2093   xen_mark_tag = Xen_make_object_type("Mark", sizeof(xen_mark));
2094 #endif
2095 #endif
2096 
2097 #if HAVE_FORTH
2098   fth_set_object_inspect(xen_mark_tag,   print_xen_mark);
2099   fth_set_object_dump(xen_mark_tag,      g_xen_mark_to_string);
2100   fth_set_object_equal(xen_mark_tag,     equalp_xen_mark);
2101   fth_set_object_free(xen_mark_tag,      free_xen_mark);
2102 #endif
2103 
2104 #if HAVE_RUBY
2105   rb_define_method(xen_mark_tag, "to_s",     Xen_procedure_cast print_xen_mark, 0);
2106   rb_define_method(xen_mark_tag, "eql?",     Xen_procedure_cast equalp_xen_mark, 1);
2107   rb_define_method(xen_mark_tag, "==",       Xen_procedure_cast equalp_xen_mark, 1);
2108   rb_define_method(xen_mark_tag, "to_str",   Xen_procedure_cast g_xen_mark_to_string, 0);
2109 #endif
2110 }
2111 /* -------------------------------------------------------------------------------- */
2112 
2113 
g_integer_to_mark(Xen n)2114 static Xen g_integer_to_mark(Xen n)
2115 {
2116   #define H_integer_to_mark "(" S_integer_to_mark " n) returns a mark object corresponding to the given integer"
2117   mark *m;
2118   Xen_check_type(Xen_is_integer(n), n, 1, S_integer_to_mark, "an integer");
2119   m = find_mark_from_id(Xen_integer_to_C_int(n), NULL, AT_CURRENT_EDIT_POSITION);
2120   if (m)
2121     return(new_xen_mark(m->id));
2122   return(Xen_false);
2123 }
2124 
2125 
g_mark_to_integer(Xen n)2126 static Xen g_mark_to_integer(Xen n)
2127 {
2128   #define H_mark_to_integer "(" S_mark_to_integer " id) returns the integer corresponding to the given mark object"
2129   Xen_check_type(xen_is_mark(n), n, 1, S_mark_to_integer, "a mark");
2130   return(C_int_to_Xen_integer(xen_mark_to_int(n)));
2131 }
2132 
2133 
snd_no_such_mark_error(const char * caller,Xen id)2134 static Xen snd_no_such_mark_error(const char *caller, Xen id)
2135 {
2136   Xen_error(Xen_make_error_type("no-such-mark"),
2137 	    Xen_list_3(C_string_to_Xen_string("~A: no such mark ~A"),
2138 		       C_string_to_Xen_string(caller),
2139 		       id));
2140   return(Xen_false);
2141 }
2142 
2143 
2144 typedef enum {MARK_SAMPLE, MARK_NAME, MARK_SYNC, MARK_HOME} mark_field_t;
2145 
mark_get(Xen n,mark_field_t fld,Xen pos_n,const char * caller)2146 static Xen mark_get(Xen n, mark_field_t fld, Xen pos_n, const char *caller)
2147 {
2148   int pos;
2149   chan_info *ncp[1];
2150   mark *m = NULL;
2151 
2152   pos = (Xen_is_integer(pos_n)) ? Xen_integer_to_C_int(pos_n) : AT_CURRENT_EDIT_POSITION;
2153 
2154   m = find_mark_from_id(Xen_mark_to_C_int(n), ncp, pos);
2155   if (!m)
2156     return(snd_no_such_mark_error(caller, n));
2157 
2158   switch (fld)
2159     {
2160     case MARK_SAMPLE:
2161       return(C_llong_to_Xen_llong(m->samp));
2162 
2163     case MARK_SYNC:
2164       return(C_int_to_Xen_integer(m->sync));
2165 
2166     case MARK_NAME:
2167       if (m->name)
2168 	return(C_string_to_Xen_string(m->name));
2169       return(C_string_to_Xen_string(""));
2170 
2171     case MARK_HOME:
2172       return(Xen_list_2(C_int_to_Xen_sound((ncp[0]->sound)->index),
2173 			C_int_to_Xen_integer(ncp[0]->chan)));
2174     }
2175   return(Xen_false);
2176 }
2177 
2178 
mark_set(Xen mark_n,Xen val,mark_field_t fld,const char * caller)2179 static Xen mark_set(Xen mark_n, Xen val, mark_field_t fld, const char *caller)
2180 {
2181   chan_info *cp[1];
2182   mark *m;
2183 
2184   m = find_mark_from_id(Xen_mark_to_C_int(mark_n), cp, AT_CURRENT_EDIT_POSITION);
2185   if (!m)
2186     return(snd_no_such_mark_error(caller, mark_n));
2187 
2188   switch (fld)
2189     {
2190     case MARK_SAMPLE:
2191       m->samp = mus_oclamp(0,
2192 			   (Xen_is_llong(val)) ? Xen_llong_to_C_llong(val) : 0,
2193 			   current_samples(cp[0]));
2194       sort_marks(cp[0]); /* update and re-sort current mark list */
2195       run_mark_hook(cp[0], m->id, MARK_MOVE);
2196       update_graph(cp[0]);
2197       break;
2198 
2199     case MARK_SYNC:
2200       if (Xen_is_integer(val))
2201 	set_mark_sync(m, Xen_integer_to_C_int(val));
2202       else set_mark_sync(m, (int)Xen_boolean_to_C_bool(val));
2203       break;
2204 
2205     case MARK_NAME:
2206       if (m->name) free(m->name);
2207       if (Xen_is_false(val))
2208 	m->name = NULL;
2209       else m->name = mus_strdup(Xen_string_to_C_string(val));
2210       update_graph(cp[0]);
2211       break;
2212 
2213     default:
2214       break;
2215     }
2216   return(val);
2217 }
2218 
2219 
g_is_mark(Xen id_n)2220 static Xen g_is_mark(Xen id_n)
2221 {
2222   #define H_is_mark "(" S_is_mark " id): " PROC_TRUE " if mark is active"
2223   if (xen_is_mark(id_n))
2224     return(C_bool_to_Xen_boolean(find_mark_from_id(Xen_mark_to_C_int(id_n), NULL, AT_CURRENT_EDIT_POSITION)));
2225   return(Xen_false);
2226 }
2227 
2228 
g_mark_sample(Xen mark_n,Xen pos_n)2229 static Xen g_mark_sample(Xen mark_n, Xen pos_n)
2230 {
2231   #define H_mark_sample "(" S_mark_sample " id :optional pos): mark's location (sample number) at edit history pos"
2232   Xen_check_type(xen_is_mark(mark_n), mark_n, 1, S_mark_sample, "a mark");
2233   Xen_check_type(Xen_is_integer_or_unbound(pos_n), pos_n, 2, S_mark_sample, "an integer");
2234   return(mark_get(mark_n, MARK_SAMPLE, pos_n, S_mark_sample));
2235 }
2236 
g_set_mark_sample(Xen mark_n,Xen samp_n)2237 static Xen g_set_mark_sample(Xen mark_n, Xen samp_n)
2238 {
2239   Xen_check_type(xen_is_mark(mark_n), mark_n, 1, S_set S_mark_sample, "a mark");
2240   Xen_check_type(Xen_is_llong(samp_n) || !Xen_is_bound(samp_n), samp_n, 2, S_set S_mark_sample, "an integer");
2241   return(mark_set(mark_n, samp_n, MARK_SAMPLE, S_set S_mark_sample));
2242 }
2243 
2244 
g_mark_sync(Xen mark_n)2245 Xen g_mark_sync(Xen mark_n)
2246 {
2247   #define H_mark_sync "(" S_mark_sync " id): mark's sync value (default: 0)"
2248   Xen_check_type(xen_is_mark(mark_n), mark_n, 1, S_mark_sync, "a mark");
2249   return(mark_get(mark_n, MARK_SYNC, Xen_undefined, S_mark_sync));
2250 }
2251 
g_set_mark_sync(Xen mark_n,Xen sync_n)2252 Xen g_set_mark_sync(Xen mark_n, Xen sync_n)
2253 {
2254   Xen_check_type(xen_is_mark(mark_n), mark_n, 1, S_set S_mark_sync, "a mark");
2255   Xen_check_type(Xen_is_integer_or_boolean(sync_n), sync_n, 2, S_set S_mark_sync, "an integer");
2256   return(mark_set(mark_n, sync_n, MARK_SYNC, S_set S_mark_sync));
2257 }
2258 
2259 
g_mark_name(Xen mark_n)2260 static Xen g_mark_name(Xen mark_n)
2261 {
2262   #define H_mark_name "(" S_mark_name " id): mark's name"
2263   Xen_check_type(xen_is_mark(mark_n), mark_n, 1, S_mark_name, "a mark");
2264   return(mark_get(mark_n, MARK_NAME, Xen_undefined, S_mark_name));
2265 }
2266 
g_set_mark_name(Xen mark_n,Xen name)2267 static Xen g_set_mark_name(Xen mark_n, Xen name)
2268 {
2269   Xen_check_type(xen_is_mark(mark_n), mark_n, 1, S_set S_mark_name, "a mark");
2270   Xen_check_type(Xen_is_string(name) || Xen_is_false(name), name, 2, S_set S_mark_name, "a string");
2271   return(mark_set(mark_n, name, MARK_NAME, S_set S_mark_name));
2272 }
2273 
2274 
g_mark_sync_max(void)2275 static Xen g_mark_sync_max(void)
2276 {
2277   #define H_mark_sync_max "(" S_mark_sync_max "): max mark sync value seen so far"
2278   return(C_int_to_Xen_integer(mark_sync_max()));
2279 }
2280 
2281 
g_mark_home(Xen mark_n)2282 static Xen g_mark_home(Xen mark_n)
2283 {
2284   #define H_mark_home "(" S_mark_home " id): the sound (index) and channel that hold mark id"
2285   Xen_check_type(xen_is_mark(mark_n), mark_n, 1, S_mark_home, "a mark");
2286   return(mark_get(mark_n, MARK_HOME, Xen_undefined, S_mark_home));
2287 }
2288 
2289 
g_find_mark(Xen samp_n,Xen snd,Xen chn_n,Xen edpos)2290 static Xen g_find_mark(Xen samp_n, Xen snd, Xen chn_n, Xen edpos)
2291 {
2292   #define H_find_mark "(" S_find_mark " samp-or-name :optional snd chn edpos): \
2293 find the mark in snd's channel chn at samp (if a number) or with the given name (if a string); return the mark or " PROC_FALSE " if no mark found."
2294 
2295   mark **mps;
2296   int pos;
2297   chan_info *cp = NULL;
2298 
2299   Xen_check_type((Xen_is_llong(samp_n) || Xen_is_string(samp_n) || (Xen_is_false(samp_n))), samp_n, 1, S_find_mark, "an integer or string or " PROC_FALSE);
2300   Snd_assert_channel(S_find_mark, snd, chn_n, 2);
2301 
2302   cp = get_cp(snd, chn_n, S_find_mark);
2303   if (!cp) return(Xen_false);
2304   pos = to_c_edit_position(cp, edpos, S_find_mark, 4);
2305 
2306   mps = cp->edits[pos]->marks;
2307   if (mps)
2308     {
2309       int i;
2310       mus_long_t samp = 0;
2311       const char *name = NULL;
2312       if (Xen_is_string(samp_n))
2313 	name = Xen_string_to_C_string(samp_n);
2314       else
2315 	{
2316 	  if (Xen_is_llong(samp_n))
2317 	    samp = Xen_llong_to_C_llong(samp_n);
2318 	}
2319       if (name)
2320 	{
2321 	  for (i = 0; i <= cp->edits[pos]->mark_ctr; i++)
2322 	    if ((mps[i]) &&
2323 		(mus_strcmp(name, mps[i]->name)))
2324 	      return(new_xen_mark(mps[i]->id));
2325 	}
2326       else
2327 	{
2328 	  for (i = 0; i <= cp->edits[pos]->mark_ctr; i++)
2329 	    if ((mps[i]) &&
2330 		(mps[i]->samp == samp))
2331 	      return(new_xen_mark(mps[i]->id));
2332 	}
2333     }
2334   return(Xen_false);
2335 }
2336 
g_add_mark_1(Xen samp_n,Xen snd,Xen chn_n,Xen name,Xen sync,bool check_sample,const char * caller)2337 static Xen g_add_mark_1(Xen samp_n, Xen snd, Xen chn_n, Xen name, Xen sync, bool check_sample, const char *caller)
2338 {
2339   #define H_add_mark "(" S_add_mark " samp :optional snd chn name (sync 0)): add a mark at sample samp returning the mark."
2340   mark *m = NULL;
2341   chan_info *cp;
2342   mus_long_t loc = 0;
2343   int msync = 0;
2344   const char *mname = NULL;
2345 
2346   Xen_check_type(Xen_is_llong(samp_n) || !Xen_is_bound(samp_n), samp_n, 1, caller, "an integer");
2347   Xen_check_type(Xen_is_string_or_unbound(name) || Xen_is_false(name), name, 4, caller, "a string");
2348   Xen_check_type(Xen_is_integer_or_unbound(sync), sync, 5, caller, "an integer");
2349   Snd_assert_channel(caller, snd, chn_n, 2);
2350 
2351   cp = get_cp(snd, chn_n, caller);
2352   if (!cp) return(Xen_false);
2353 
2354   if (Xen_is_llong(samp_n)) loc = Xen_llong_to_C_llong(samp_n);
2355 
2356   if ((!check_sample) &&
2357       (loc >= current_samples(cp)))
2358     return(Xen_false);
2359 
2360   if ((loc < 0) ||
2361       (loc >= current_samples(cp)))
2362     Xen_error(Xen_make_error_type("no-such-sample"),
2363 	      Xen_list_2(C_string_to_Xen_string(S_add_mark ": no such sample, ~A"),
2364 			 samp_n));
2365 
2366   if (Xen_is_string(name)) mname = Xen_string_to_C_string(name);
2367   if (Xen_is_integer(sync)) msync = Xen_integer_to_C_int(sync);
2368 
2369   m = add_mark(loc, mname, cp);
2370   if (m)
2371     {
2372       if (msync != 0) set_mark_sync(m, msync);
2373       update_graph(cp);
2374       return(new_xen_mark(m->id));
2375     }
2376   return(Xen_false);
2377 }
2378 
2379 
g_add_mark(Xen samp_n,Xen snd,Xen chn_n,Xen name,Xen sync)2380 static Xen g_add_mark(Xen samp_n, Xen snd, Xen chn_n, Xen name, Xen sync)
2381 {
2382   return(g_add_mark_1(samp_n, snd, chn_n, name, sync, true, S_add_mark));
2383 }
2384 
2385 
g_add_mark_unchecked(Xen samp_n,Xen snd,Xen chn_n,Xen name,Xen sync)2386 static Xen g_add_mark_unchecked(Xen samp_n, Xen snd, Xen chn_n, Xen name, Xen sync)
2387 {
2388   #define H_add_mark_unchecked "(add-mark! samp :optional snd chn name (sync 0)): add a mark at sample samp returning the mark.\
2389 Unlike add-mark, add-mark! does not check for an invalid sample number."
2390 
2391   return(g_add_mark_1(samp_n, snd, chn_n, name, sync, false, S_add_mark "!"));
2392 }
2393 
2394 
g_delete_mark(Xen id_n)2395 static Xen g_delete_mark(Xen id_n)
2396 {
2397   #define H_delete_mark "(" S_delete_mark " id): delete mark id"
2398   chan_info *cp[1];
2399   mark *m;
2400   int id;
2401 
2402   Xen_check_type(xen_is_mark(id_n), id_n, 1, S_delete_mark, "a mark");
2403   id = Xen_mark_to_C_int(id_n);
2404 
2405   m = find_mark_from_id(id, cp, AT_CURRENT_EDIT_POSITION);
2406   if (!m)
2407     return(snd_no_such_mark_error(S_delete_mark, id_n));
2408 
2409   if (delete_mark_id(id, cp[0]))
2410     update_graph(cp[0]);
2411   else return(snd_no_such_mark_error(S_delete_mark, id_n));
2412 
2413   return(id_n);
2414 }
2415 
2416 
g_delete_marks(Xen snd,Xen chn_n)2417 static Xen g_delete_marks(Xen snd, Xen chn_n)
2418 {
2419   #define H_delete_marks "(" S_delete_marks " :optional snd chn): delete all marks in snd's channel chn"
2420   chan_info *cp;
2421   Snd_assert_channel(S_delete_marks, snd, chn_n, 1);
2422   cp = get_cp(snd, chn_n, S_delete_marks);
2423   if (!cp) return(Xen_false);
2424   delete_marks(cp);
2425   return(Xen_false);
2426 }
2427 
2428 
int_array_to_mark_list(int * arr,int i,int len)2429 static Xen int_array_to_mark_list(int *arr, int i, int len)
2430 {
2431   if (i < len)
2432     return(Xen_cons(new_xen_mark(arr[i]), int_array_to_mark_list(arr, i + 1, len)));
2433   return(Xen_cons(new_xen_mark(arr[i]), Xen_empty_list));
2434 }
2435 
2436 
syncd_marks(int sync)2437 static int *syncd_marks(int sync)
2438 {
2439   syncdata *sd;
2440   int *ids;
2441   int i;
2442   sd = make_syncdata(sync);
2443   for_each_normal_chan_with_void(gather_chan_syncd_marks, (void *)sd);
2444   ids = (int *)calloc(1 + sd->mark_ctr, sizeof(int));
2445   ids[0] = sd->mark_ctr;
2446   for (i = 0; i < sd->mark_ctr; i++) ids[i + 1] = sd->marks[i]->id;
2447   free_syncdata(sd);
2448   return(ids);
2449 }
2450 
2451 
g_syncd_marks(Xen sync)2452 static Xen g_syncd_marks(Xen sync)
2453 {
2454   #define H_syncd_marks "(" S_syncd_marks " sync): list of marks that share a given sync value (" S_mark_sync ")"
2455   int *ids;
2456   Xen res;
2457 
2458   Xen_check_type(Xen_is_integer(sync), sync, 1, S_syncd_marks, "an integer");
2459   ids = syncd_marks(Xen_integer_to_C_int(sync));
2460 
2461   if (!ids) return(Xen_empty_list);
2462   if (ids[0] == 0) {free(ids); return(Xen_empty_list);}
2463 
2464   res = int_array_to_mark_list(ids, 1, ids[0]);
2465 
2466   free(ids);
2467   return(res);
2468 }
2469 
2470 
g_mark_tag_width(void)2471 static Xen g_mark_tag_width(void) {return(C_int_to_Xen_integer(mark_tag_width(ss)));}
2472 
g_set_mark_tag_width(Xen val)2473 static Xen g_set_mark_tag_width(Xen val)
2474 {
2475   #define H_mark_tag_width "(" S_mark_tag_width "): width (pixels) of mark tags (10)"
2476   int width;
2477   Xen_check_type(Xen_is_integer(val), val, 1, S_set S_mark_tag_width, "an integer");
2478   width = mus_iclamp(0, Xen_integer_to_C_int(val), LOTSA_PIXELS);
2479   set_mark_tag_width(width);
2480   for_each_normal_chan(update_graph);
2481   return(C_int_to_Xen_integer(mark_tag_width(ss)));
2482 }
2483 
2484 
g_mark_tag_height(void)2485 static Xen g_mark_tag_height(void) {return(C_int_to_Xen_integer(mark_tag_height(ss)));}
2486 
g_set_mark_tag_height(Xen val)2487 static Xen g_set_mark_tag_height(Xen val)
2488 {
2489   #define H_mark_tag_height "(" S_mark_tag_height "): height (pixels) of mark tags (4)"
2490   int height;
2491   Xen_check_type(Xen_is_integer(val), val, 1, S_set S_mark_tag_height, "an integer");
2492   height = mus_iclamp(0, Xen_integer_to_C_int(val), LOTSA_PIXELS);
2493   set_mark_tag_height(height);
2494   for_each_normal_chan(update_graph);
2495   return(C_int_to_Xen_integer(mark_tag_height(ss)));
2496 }
2497 
2498 
channel_marks(chan_info * cp,int pos)2499 static int *channel_marks(chan_info *cp, int pos)
2500 {
2501   int *ids = NULL;
2502   ed_list *ed;
2503   ed = cp->edits[pos];
2504   if (ed->marks)
2505     {
2506       mark **mps;
2507       int marks;
2508       mps = ed->marks;
2509       marks = ed->mark_ctr;
2510       if (mps)
2511 	{
2512 	  int i;
2513 	  ids = (int *)calloc(marks + 2, sizeof(int)); /* 1 for size, 1 because mark_ctr is current count */
2514 	  ids[0] = marks + 1;
2515 	  for (i = 0; i <= marks; i++)
2516 	    ids[i + 1] = mps[i]->id;
2517 	}
2518     }
2519   return(ids);
2520 }
2521 
2522 
g_marks(Xen snd,Xen chn_n,Xen pos_n)2523 static Xen g_marks(Xen snd, Xen chn_n, Xen pos_n)
2524 {
2525   #define H_marks "(" S_marks " :optional snd chn edpos): list of marks in snd/chn at edit history position pos. \
2526 mark list is: channel given: (id id ...), snd given: ((id id) (id id ...)), neither given: (((id ...) ...) ...)."
2527 
2528   chan_info *cp;
2529   snd_info *sp;
2530   Xen res1 = Xen_empty_list;
2531 
2532   Snd_assert_channel(S_marks, snd, chn_n, 0);
2533 
2534   if (Xen_is_integer(snd) || xen_is_sound(snd))
2535       {
2536 	int *ids;
2537 	if (Xen_is_integer(chn_n))
2538 	  {
2539 	    int pos;
2540 	    Xen res;
2541 	    cp = get_cp(snd, chn_n, S_marks);
2542 	    if (!cp) return(Xen_false);
2543 	    if (Xen_is_integer(pos_n))
2544 	      {
2545 		pos = Xen_integer_to_C_int(pos_n);
2546 		if (pos == AT_CURRENT_EDIT_POSITION)
2547 		  pos = cp->edit_ctr;
2548 		if ((pos < 0) || (pos >= cp->edit_size) || (!cp->edits[pos]))
2549 		  Xen_error(Xen_make_error_type("no-such-edit"),
2550 			    Xen_list_2(C_string_to_Xen_string(S_marks ": no such edit, ~A"),
2551 				       pos_n));
2552 	      }
2553 	    else pos = cp->edit_ctr;
2554 	    ids = channel_marks(cp, pos);
2555 	    if (!ids) return(Xen_empty_list);
2556 	    if (ids[0] == 0)
2557 	      {
2558 		free(ids);
2559 		return(Xen_empty_list);
2560 	      }
2561 	    res = int_array_to_mark_list(ids, 1, ids[0]);
2562 	    free(ids);
2563 	    return(res);
2564 	  }
2565 	else
2566 	  {
2567 	    int i;
2568 	    sp = get_sp(snd);
2569 	    if (!sp)
2570 	      return(snd_no_such_sound_error(S_marks, snd));
2571 	    for (i = (int)sp->nchans - 1; i >= 0; i--)
2572 	      {
2573 		cp = sp->chans[i];
2574 		ids = channel_marks(cp, cp->edit_ctr);
2575 		if ((!ids) || (ids[0] == 0))
2576 		  res1 = Xen_cons(Xen_empty_list, res1);
2577 		else res1 = Xen_cons(int_array_to_mark_list(ids, 1, ids[0]),
2578 				     res1);
2579 		if (ids) free(ids);
2580 	      }
2581 	  }
2582       }
2583   else
2584     {
2585       int j;
2586       /* all marks */
2587       for (j = ss->max_sounds - 1; j >= 0; j--)
2588 	{
2589 	  sp = ss->sounds[j];
2590 	  if ((sp) && (sp->inuse == SOUND_NORMAL))
2591 	    res1 = Xen_cons(g_marks(C_int_to_Xen_integer(j), Xen_undefined, Xen_undefined),
2592 			    res1);
2593 	}
2594     }
2595   return(res1);
2596 }
2597 
2598 
2599 /* ---------------- saved marks ----------------
2600  *
2601  * upon restoration, pre-existing marks can collide both in mark-id and mark-sync
2602  *   with the saved marks, so these need to be fixed-up as they are encountered.
2603  *   Also mark-sync-max needs to reflect the newly chosen sync values.
2604  *
2605  * We used to try to save the entire mark history, but I can't see how that can work
2606  *   in general -- the save-marks output can be loaded at any time, so we can't
2607  *   rely on anything in regard to the edit history.
2608  */
2609 
find_any_marks(chan_info * cp)2610 static bool find_any_marks(chan_info *cp)
2611 {
2612   ed_list *ed;
2613   ed = cp->edits[cp->edit_ctr];
2614   return((ed->marks) && (ed->mark_ctr >= 0)); /* initialized to -1 -- 0 is first mark */
2615 }
2616 
2617 typedef struct {
2618   FILE *fd;
2619   int size;
2620   int *syncs;
2621 } save_mark_info;
2622 
2623 #define SYNC_BASE "_sync_"
2624 #define SYNC_NAME_SIZE 32
2625 
mark_sync_name(int cur_sync)2626 static char *mark_sync_name(int cur_sync)
2627 {
2628   char *result;
2629   result = (char *)calloc(SYNC_NAME_SIZE, sizeof(char));
2630   snprintf(result, SYNC_NAME_SIZE, "%s%d", SYNC_BASE, cur_sync);
2631   return(result);
2632 }
2633 
2634 
map_mark_sync(chan_info * cp,mark * m,save_mark_info * sv)2635 static char *map_mark_sync(chan_info *cp, mark *m, save_mark_info *sv)
2636 {
2637   /* if sync 0 just return "0",  else if already in syncs array, use _sync_n_ as name,
2638    *   else open a let, declare _sync_n_ with new safe value,
2639    *   add current int value to syncs array (can't assume unshared here will not collide later)
2640    */
2641 
2642   int i, cur_sync;
2643   cur_sync = m->sync;
2644   if (cur_sync == 0)
2645     return(mus_strdup("0"));
2646 
2647   if (sv->size > 0)
2648     for (i = 0; i < sv->size; i++)
2649       if (cur_sync == sv->syncs[i])
2650 	return(mark_sync_name(cur_sync));
2651 
2652   /* add sync to current set (protect against later collisions, take current shared-syncs into account) */
2653   if (sv->size == 0)
2654     sv->syncs = (int *)calloc(1, sizeof(int));
2655   else sv->syncs = (int *)realloc(sv->syncs, (sv->size + 1) * sizeof(int));
2656   sv->syncs[sv->size++] = cur_sync;
2657 
2658 #if (!HAVE_FORTH)
2659   fprintf(sv->fd, "      ");
2660   for (i = 0; i < sv->size - 1; i++) fprintf(sv->fd, "  "); /* previous lets */
2661 #endif
2662 
2663 #if HAVE_SCHEME
2664   fprintf(sv->fd, "(let ((%s%d (+ (mark-sync-max) 1)))\n", SYNC_BASE, cur_sync);
2665 #endif
2666 
2667 #if HAVE_RUBY
2668   fprintf(sv->fd, "begin\n        %s%d = mark_sync_max + 1\n", SYNC_BASE, cur_sync);
2669 #endif
2670 
2671 #if HAVE_FORTH
2672   fprintf(sv->fd, "mark-sync-max 1 + value %s%d\n", SYNC_BASE, cur_sync);
2673 #endif
2674 
2675   return(mark_sync_name(cur_sync));
2676 }
2677 
2678 
save_mark(chan_info * cp,mark * m,void * info)2679 static mark *save_mark(chan_info *cp, mark *m, void *info)
2680 {
2681   /* we're called within "sound_block" where "sfile" = current sound index */
2682 
2683   save_mark_info *sv = (save_mark_info *)info;
2684   char *mapped_sync;
2685   int i;
2686 
2687   mapped_sync = map_mark_sync(cp, m, sv);
2688 
2689   fprintf(sv->fd, "      ");
2690   for (i = 0; i < sv->size; i++) fprintf(sv->fd, "  "); /* lets */
2691 
2692   /* here we need to use the "!" form of add-mark to ignore bad sample numbers -- these can come about during
2693    *   the save-state process when redoable edits causing extensions are undone before marks are added,
2694    *   possibly to the extended portion -- we'll simply drop those marks, rather than wrecking the restore
2695    *   process with an error.
2696    */
2697 
2698 #if HAVE_SCHEME
2699   if (m->name)
2700     fprintf(sv->fd, "(add-mark! %" print_mus_long " sfile %d \"%s\" %s)\n", m->samp, cp->chan, m->name, mapped_sync);
2701   else fprintf(sv->fd, "(add-mark! %" print_mus_long " sfile %d #f %s)\n", m->samp, cp->chan, mapped_sync);
2702 #endif
2703 
2704 #if HAVE_RUBY
2705   if (m->name)
2706     fprintf(sv->fd, "add_mark!(%" print_mus_long ", sfile, %d, \"%s\", %s)\n", m->samp, cp->chan, m->name, mapped_sync);
2707   else fprintf(sv->fd, "add_mark!(%" print_mus_long ", sfile, %d, false, %s)\n", m->samp, cp->chan, mapped_sync);
2708 #endif
2709 
2710 #if HAVE_FORTH
2711   if (m->name)
2712     fprintf(sv->fd, "%" print_mus_long " sfile %d \"%s\" %s add-mark! drop\n", m->samp, cp->chan, m->name, mapped_sync);
2713   else fprintf(sv->fd, "%" print_mus_long " sfile %d #f %s add-mark! drop\n", m->samp, cp->chan, mapped_sync);
2714 #endif
2715 
2716   free(mapped_sync);
2717   return(NULL); /* returning a mark here breaks out of the map mark loop */
2718 }
2719 
2720 
save_mark_list(FILE * fd,chan_info * cp,bool all_chans)2721 void save_mark_list(FILE *fd, chan_info *cp, bool all_chans)
2722 {
2723   /* used in save-marks (below) and the edit history stuff in snd-edits.c
2724    *   changed 23-Sep-06 -- restore-marks is now a no-op, and no attempt is made to save the entire mark history.
2725    */
2726 
2727   save_mark_info *sv;
2728   if ((!all_chans) && (!(find_any_marks(cp)))) return; /* in the sound (all_chans) case, this has been checked already */
2729   sv = (save_mark_info *)calloc(1, sizeof(save_mark_info));
2730   sv->fd = fd;
2731   sv->size = 0;
2732   sv->syncs = NULL;
2733   if (all_chans)
2734     {
2735       uint32_t i;
2736       snd_info *sp;
2737       sp = cp->sound;
2738       for (i = 0; i < sp->nchans; i++)
2739 	map_over_marks(sp->chans[i], save_mark, (void *)sv, READ_FORWARD);
2740     }
2741   else map_over_marks(cp, save_mark, (void *)sv, READ_FORWARD);
2742   if (sv->size > 0)
2743     {
2744 #if HAVE_SCHEME || HAVE_RUBY
2745       int i;
2746 #endif
2747       fprintf(fd, "      ");
2748 
2749 #if HAVE_SCHEME
2750       for (i = 0; i < sv->size; i++) fprintf(fd, ")");
2751 #endif
2752 
2753 #if HAVE_RUBY
2754       for (i = 0; i < sv->size; i++) fprintf(fd, "end\n");
2755 #endif
2756 
2757       fprintf(fd, "\n");
2758     }
2759   if (sv->syncs) free(sv->syncs);
2760   free(sv);
2761 }
2762 
2763 
g_save_marks(Xen snd,Xen filename)2764 static Xen g_save_marks(Xen snd, Xen filename)
2765 {
2766   #define H_save_marks "(" S_save_marks " :optional snd (filename \"<snd-file-name>.marks\")): save snd's marks in filename. \
2767 The saved file is " Xen_language " code, so to restore the marks, load that file."
2768 
2769   snd_info *sp;
2770   Xen res = Xen_false;
2771 
2772   Snd_assert_sound(S_save_marks, snd, 1);
2773   Xen_check_type(Xen_is_string_or_unbound(filename), filename, 2, S_save_marks, "a string");
2774 
2775   sp = get_sp(snd);
2776   if (!sp)
2777     return(snd_no_such_sound_error(S_save_marks, snd));
2778 
2779   if (map_over_sound_chans(sp, find_any_marks)) /* are there any marks? */
2780     {
2781       char *newname = NULL;
2782       FILE *fd;
2783       if (Xen_is_string(filename))
2784 	newname = mus_strdup(Xen_string_to_C_string(filename));
2785       else
2786 	{
2787 	  int i, len;
2788 	  len = strlen(sp->filename);
2789 	  newname = (char *)calloc(len + 7, sizeof(char));
2790 	  strcopy(newname, sp->filename, len + 7);
2791 	  for (i = len - 1; i > 0; i--)
2792 	    if (newname[i] == '.')
2793 	      break;
2794 	  if (i > 0) len = i;
2795 	  newname[len] = '\0';
2796 	  strcat(newname, ".marks");
2797 	}
2798       fd = FOPEN(newname, "w");
2799       if (!fd)
2800 	{
2801 	  Xen lname;
2802 	  lname = C_string_to_Xen_string(newname);
2803 	  free(newname);
2804 	  Xen_error(Xen_make_error_type("cannot-save"),
2805 		    Xen_list_3(C_string_to_Xen_string(S_save_marks ": can't save ~S, ~A"),
2806 			       lname,
2807 			       C_string_to_Xen_string(snd_open_strerror())));
2808 	}
2809       else
2810 	{
2811 #if HAVE_FORTH
2812 	  fprintf(fd, "#f value sfile\n");
2813 #endif
2814 	  open_save_sound_block(sp, fd, false);   /* find-sound or open it with enclosing let */
2815 	  save_mark_list(fd, sp->chans[0], true); /* true -> save all chans, matching cross-channel syncs */
2816 	  close_save_sound_block(fd, false);      /* close the let form */
2817 	  snd_fclose(fd, newname);
2818 	  res = C_string_to_Xen_string(newname);
2819 	}
2820       free(newname);
2821     }
2822   return(res);
2823 }
2824 
2825 
g_mark_properties(Xen n)2826 static Xen g_mark_properties(Xen n)
2827 {
2828   mark *m;
2829   #define H_mark_properties "(" S_mark_properties " id):  A property list associated with the given mark."
2830 
2831   Xen_check_type(xen_is_mark(n), n, 1, S_mark_properties, "a mark");
2832 
2833   m = find_mark_from_id(Xen_mark_to_C_int(n), NULL, AT_CURRENT_EDIT_POSITION);
2834   if (!m)
2835     return(snd_no_such_mark_error(S_mark_properties, n));
2836 
2837   if (!(Xen_is_vector(m->properties)))
2838     {
2839       m->properties = Xen_make_vector(1, Xen_empty_list);
2840       m->properties_gc_loc = snd_protect(m->properties);
2841     }
2842   return(Xen_vector_ref(m->properties, 0));
2843 }
2844 
2845 
g_set_mark_properties(Xen n,Xen val)2846 static Xen g_set_mark_properties(Xen n, Xen val)
2847 {
2848   mark *m;
2849   Xen_check_type(xen_is_mark(n), n, 1, S_mark_properties, "a mark");
2850 
2851   m = find_mark_from_id(Xen_mark_to_C_int(n), NULL, AT_CURRENT_EDIT_POSITION);
2852   if (!m)
2853     return(snd_no_such_mark_error(S_set S_mark_properties, n));
2854 
2855   if (!(Xen_is_vector(m->properties)))
2856     {
2857       m->properties = Xen_make_vector(1, Xen_empty_list);
2858       m->properties_gc_loc = snd_protect(m->properties);
2859     }
2860 
2861   Xen_vector_set(m->properties, 0, val);
2862   return(Xen_vector_ref(m->properties, 0));
2863 }
2864 
2865 
g_mark_property(Xen key,Xen id)2866 static Xen g_mark_property(Xen key, Xen id)
2867 {
2868   #define H_mark_property "(" S_mark_property " key id) returns the value associated with 'key' in the given mark's property list, or " PROC_FALSE "."
2869   Xen_check_type(xen_is_mark(id), id, 2, S_mark_property, "a mark");
2870   return(Xen_assoc_ref(key, g_mark_properties(id)));
2871 }
2872 
g_set_mark_property(Xen key,Xen id,Xen val)2873 static Xen g_set_mark_property(Xen key, Xen id, Xen val)
2874 {
2875   Xen_check_type(xen_is_mark(id), id, 2, S_mark_property, "a mark");
2876   g_set_mark_properties(id, Xen_assoc_set(key, val, g_mark_properties(id)));
2877   return(val);
2878 }
2879 
2880 
2881 
Xen_wrap_2_optional_args(g_mark_sample_w,g_mark_sample)2882 Xen_wrap_2_optional_args(g_mark_sample_w, g_mark_sample)
2883 Xen_wrap_2_args(g_set_mark_sample_w, g_set_mark_sample)
2884 Xen_wrap_1_arg(g_mark_sync_w, g_mark_sync)
2885 Xen_wrap_2_args(g_set_mark_sync_w, g_set_mark_sync)
2886 Xen_wrap_1_arg(g_mark_name_w, g_mark_name)
2887 Xen_wrap_2_args(g_set_mark_name_w, g_set_mark_name)
2888 Xen_wrap_no_args(g_mark_sync_max_w, g_mark_sync_max)
2889 Xen_wrap_1_arg(g_mark_home_w, g_mark_home)
2890 Xen_wrap_3_optional_args(g_marks_w, g_marks)
2891 Xen_wrap_5_optional_args(g_add_mark_w, g_add_mark)
2892 Xen_wrap_5_optional_args(g_add_mark_unchecked_w, g_add_mark_unchecked)
2893 Xen_wrap_1_arg(g_delete_mark_w, g_delete_mark)
2894 Xen_wrap_2_optional_args(g_delete_marks_w, g_delete_marks)
2895 Xen_wrap_1_arg(g_syncd_marks_w, g_syncd_marks)
2896 Xen_wrap_no_args(g_mark_tag_width_w, g_mark_tag_width)
2897 Xen_wrap_1_arg(g_set_mark_tag_width_w, g_set_mark_tag_width)
2898 Xen_wrap_no_args(g_mark_tag_height_w, g_mark_tag_height)
2899 Xen_wrap_1_arg(g_set_mark_tag_height_w, g_set_mark_tag_height)
2900 Xen_wrap_4_optional_args(g_find_mark_w, g_find_mark)
2901 Xen_wrap_2_optional_args(g_save_marks_w, g_save_marks)
2902 Xen_wrap_1_arg(g_is_mark_w, g_is_mark)
2903 Xen_wrap_1_arg(g_integer_to_mark_w, g_integer_to_mark)
2904 Xen_wrap_1_arg(g_mark_to_integer_w, g_mark_to_integer)
2905 Xen_wrap_1_arg(g_mark_properties_w, g_mark_properties)
2906 Xen_wrap_2_args(g_set_mark_properties_w, g_set_mark_properties)
2907 Xen_wrap_2_args(g_mark_property_w, g_mark_property)
2908 Xen_wrap_3_args(g_set_mark_property_w, g_set_mark_property)
2909 
2910 #if HAVE_SCHEME
2911 static s7_pointer acc_mark_tag_height(s7_scheme *sc, s7_pointer args) {return(g_set_mark_tag_height(s7_cadr(args)));}
acc_mark_tag_width(s7_scheme * sc,s7_pointer args)2912 static s7_pointer acc_mark_tag_width(s7_scheme *sc, s7_pointer args) {return(g_set_mark_tag_width(s7_cadr(args)));}
2913 
s7_mark_to_let(s7_scheme * sc,s7_pointer args)2914 static s7_pointer s7_mark_to_let(s7_scheme *sc, s7_pointer args)
2915 {
2916   /* this is called upon (object->let <mark>) */
2917   s7_pointer mark, env, val;
2918   mark = s7_car(args);
2919   env = s7_cadr(args);
2920   val = mark_get(mark, MARK_NAME, Xen_undefined, S_mark_name);
2921   if ((s7_is_string(val)) &&
2922       (s7_string_length(val) > 0))
2923     s7_varlet(sc, env, s7_make_symbol(sc, "name"), val);
2924   s7_varlet(sc, env, s7_make_symbol(sc, "sample"), mark_get(mark, MARK_SAMPLE, Xen_undefined, S_mark_sample));
2925   s7_varlet(sc, env, s7_make_symbol(sc, "sync"), mark_get(mark, MARK_SYNC, Xen_undefined, S_mark_sync));
2926   s7_varlet(sc, env, s7_make_symbol(sc, "home"), mark_get(mark, MARK_HOME, Xen_undefined, S_mark_home));
2927   return(env);
2928 }
2929 #endif
2930 
g_init_marks(void)2931 void g_init_marks(void)
2932 {
2933   #define H_mark_drag_hook S_mark_drag_hook " (id): called when a mark is dragged"
2934   #define H_mark_hook S_mark_hook " (id snd chn reason): called when a mark added, deleted, or moved. \
2935 'Reason' can be 0: add, 1: delete, 2: move, 3: delete all marks"
2936 
2937 #if HAVE_SCHEME
2938   s7_pointer i, m, b, s, p, t, pl_im, pl_add, pl_i;
2939   i = s7_make_symbol(s7, "integer?");
2940   m = s7_make_symbol(s7, "mark?");
2941   b = s7_make_symbol(s7, "boolean?");
2942   s = s7_make_symbol(s7, "string?");
2943   p = s7_make_symbol(s7, "list?");
2944   t = s7_t(s7);
2945   pl_im = s7_make_signature(s7, 2, i, m);
2946   pl_add = s7_make_signature(s7, 6, s7_make_signature(s7, 2, m, b), i, t, t, s7_make_signature(s7, 2, s, b), i);
2947   pl_i = s7_make_circular_signature(s7, 0, 1, i);
2948 
2949   mark_to_let_func = s7_make_function(s7, "mark->let", s7_mark_to_let, 2, 0, false, "mark->let");
2950 #endif
2951 
2952   init_xen_mark();
2953 
2954   mark_drag_hook = Xen_define_hook(S_mark_drag_hook, "(make-hook 'id)", 1, H_mark_drag_hook);
2955   mark_hook = Xen_define_hook(S_mark_hook, "(make-hook 'id 'snd 'chn 'reason)", 4, H_mark_hook);
2956 
2957   Xen_define_typed_dilambda(S_mark_sample, g_mark_sample_w, H_mark_sample, S_set S_mark_sample, g_set_mark_sample_w, 1, 1, 2, 0,
2958 			    s7_make_signature(s7, 3, i, m, i), s7_make_signature(s7, 3, i, m, i));
2959   /* this is bad: the second arg to mark-sample is edit-position, but to set_mark_sample is the new sample! */
2960   Xen_define_typed_dilambda(S_mark_sync, g_mark_sync_w, H_mark_sync, S_set S_mark_sync, g_set_mark_sync_w, 1, 0, 2, 0, pl_im,
2961 			    s7_make_signature(s7, 3, i, m, s7_make_signature(s7, 2, i, b)));
2962   Xen_define_typed_dilambda(S_mark_name, g_mark_name_w, H_mark_name, S_set S_mark_name, g_set_mark_name_w, 1, 0, 2, 0,
2963 			    s7_make_signature(s7, 2, s, m), s7_make_signature(s7, 3, s, m, s));
2964 
2965   Xen_define_typed_procedure(S_mark_sync_max,   g_mark_sync_max_w,   0, 0, 0, H_mark_sync_max,    s7_make_signature(s7, 1, i));
2966   Xen_define_typed_procedure(S_mark_home,       g_mark_home_w,       1, 0, 0, H_mark_home,        s7_make_signature(s7, 2, p, m));
2967   Xen_define_typed_procedure(S_marks,           g_marks_w,           0, 3, 0, H_marks,            s7_make_circular_signature(s7, 1, 2, p, t));
2968   Xen_define_typed_procedure(S_add_mark,        g_add_mark_w,        0, 5, 0, H_add_mark,         pl_add);
2969   Xen_define_typed_procedure(S_add_mark "!",    g_add_mark_unchecked_w, 0, 5, 0, H_add_mark_unchecked, pl_add);
2970   Xen_define_typed_procedure(S_delete_mark,     g_delete_mark_w,     1, 0, 0, H_delete_mark,      s7_make_signature(s7, 2, t, m));
2971   Xen_define_typed_procedure(S_delete_marks,    g_delete_marks_w,    0, 2, 0, H_delete_marks,     s7_make_signature(s7, 3, b, t, t));
2972   Xen_define_typed_procedure(S_syncd_marks,     g_syncd_marks_w,     1, 0, 0, H_syncd_marks,      s7_make_signature(s7, 2, p, i));
2973   Xen_define_typed_procedure(S_find_mark,       g_find_mark_w,       1, 3, 0, H_find_mark,
2974 			     s7_make_circular_signature(s7, 2, 3, s7_make_signature(s7, 2, m, b), s7_make_signature(s7, 2, i, s), t));
2975   Xen_define_typed_procedure(S_save_marks,      g_save_marks_w,      0, 2, 0, H_save_marks,       s7_make_signature(s7, 3, s, t, s));
2976   Xen_define_typed_procedure(S_is_mark,         g_is_mark_w,         1, 0, 0, H_is_mark,          s7_make_signature(s7, 2, b, t));
2977   Xen_define_typed_procedure(S_integer_to_mark, g_integer_to_mark_w, 1, 0, 0, H_integer_to_mark,  s7_make_signature(s7, 2, m, i));
2978   Xen_define_typed_procedure(S_mark_to_integer, g_mark_to_integer_w, 1, 0, 0, H_mark_to_integer,  s7_make_signature(s7, 2, i, m));
2979 
2980   Xen_define_typed_dilambda(S_mark_tag_width, g_mark_tag_width_w, H_mark_tag_width, S_set S_mark_tag_width, g_set_mark_tag_width_w, 0, 0, 1, 0, pl_i, pl_i);
2981   Xen_define_typed_dilambda(S_mark_tag_height, g_mark_tag_height_w, H_mark_tag_height, S_set S_mark_tag_height, g_set_mark_tag_height_w, 0, 0, 1, 0, pl_i, pl_i);
2982   Xen_define_typed_dilambda(S_mark_properties, g_mark_properties_w, H_mark_properties, S_set S_mark_properties, g_set_mark_properties_w, 1, 0, 2, 0,
2983 			    s7_make_signature(s7, 2, p, m), s7_make_signature(s7, 3, p, m, p));
2984   Xen_define_typed_dilambda(S_mark_property, g_mark_property_w, H_mark_property, S_set S_mark_property, g_set_mark_property_w, 2, 0, 3, 0,
2985 			    s7_make_signature(s7, 3, t, t, m), s7_make_circular_signature(s7, 3, 4, t, t, m, t));
2986 
2987   #define H_draw_mark_hook S_draw_mark_hook " (id): called before a mark is drawn. \
2988 If the hook returns " PROC_TRUE ", the mark is not drawn."
2989 
2990   draw_mark_hook = Xen_define_hook(S_draw_mark_hook, "(make-hook 'id)", 1, H_draw_mark_hook);
2991 
2992 #if HAVE_SCHEME
2993   s7_set_setter(s7, ss->mark_tag_height_symbol, s7_make_function(s7, "[acc-" S_mark_tag_height "]", acc_mark_tag_height, 2, 0, false, "accessor"));
2994   s7_set_setter(s7, ss->mark_tag_width_symbol, s7_make_function(s7, "[acc-" S_mark_tag_width "]", acc_mark_tag_width, 2, 0, false, "accessor"));
2995 
2996   s7_set_documentation(s7, ss->mark_tag_height_symbol, "*mark-tag-height*: height (pixels) of mark tags (4)");
2997   s7_set_documentation(s7, ss->mark_tag_width_symbol, "*mark-tag-width*: width (pixels) of mark tags (10)");
2998 #endif
2999 }
3000 
3001