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 *)(¤t_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 *)(¤t_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