1 #include "snd.h"
2 #include "clm2xen.h"
3 
4 /*
5  * each channel currently being played has an associated dac_info struct
6  *   all active dac_info structs are held in a play_list
7  *   channels can come and go as a play is in progress
8  */
9 
10 /* -------------------------------- per-channel control-panel state -------------------------------- */
11 
12 typedef struct {
13   mus_any *gen;
14   struct dac_info *dp;
15   bool speeding;
16   mus_float_t sr;
17 } spd_info;
18 
19 typedef enum {DAC_NOTHING, DAC_CHANNEL, DAC_REGION, DAC_MIX, DAC_XEN} dac_source_t;
20 
21 typedef struct dac_info {
22   dac_source_t type;
23   mus_float_t cur_index;
24   mus_float_t cur_amp;
25   mus_float_t cur_srate;
26   mus_float_t cur_exp;
27   mus_float_t cur_rev;       /* rev scaler -- len is set at initialization */
28   mus_float_t contrast_amp;
29   bool expanding, reverbing, filtering; /* these need lots of preparation, so they're noticed only at the start */
30   int audio_chan;      /* where channel's output is going (wrap-around if not enough audio output channels) */
31   int slot;
32   mus_float_t *a;      /* filter coeffs */
33   int a_size;          /* user can change filter order while playing (sigh...) */
34   snd_fd *chn_fd;      /* sampler, null if DAC_Xen */
35   spd_info *spd;
36   mus_any *flt;
37   int region, mix_id;  /* to reset region-browser play button upon completion */
38   bool selection;
39   mus_any *src;
40   snd_info *sp;        /* needed to see button callback changes etc */
41   chan_info *cp;
42   bool never_sped;
43   int expand_ring_framples;
44   mus_long_t end;
45   Xen stop_procedure;
46   int stop_procedure_gc_loc;
47   Xen func;
48   int func_gc_loc, direction;
49   mus_float_t (*dac_sample)(struct dac_info *dp);
50 } dac_info;
51 
52 #define AMP_CONTROL(sp, dp) ((dp->cp->amp_control) ? (dp->cp->amp_control[0]) : sp->amp_control)
53 /* an experiment */
54 
55 
dac_read_sample(struct dac_info * dp)56 static mus_float_t dac_read_sample(struct dac_info *dp)
57 {
58   return(read_sample(dp->chn_fd));
59 }
60 
61 
dac_no_sample(struct dac_info * dp)62 static mus_float_t dac_no_sample(struct dac_info *dp)
63 {
64   return(0.0);
65 }
66 
67 
dac_xen_sample(struct dac_info * dp)68 static mus_float_t dac_xen_sample(struct dac_info *dp)
69 {
70   Xen result;
71   result = Xen_unprotected_call_with_no_args(dp->func);
72   if (Xen_is_false(result))
73     {
74       dp->end = 0;
75       return(0.0);
76     }
77   return(Xen_real_to_C_double(result));
78 }
79 
80 
81 /* -------- filter -------- */
82 
make_flt(dac_info * dp,int order,mus_float_t * env)83 static mus_any *make_flt(dac_info *dp, int order, mus_float_t *env)
84 {
85   if (order <= 0) return(NULL);
86   dp->a_size = order;
87   dp->a = (mus_float_t *)calloc(order, sizeof(mus_float_t));
88   if (env) mus_make_fir_coeffs(order, env, dp->a);
89   return(mus_make_fir_filter(order, dp->a, NULL));
90 }
91 
92 
93 /* -------- sample-rate conversion -------- */
94 
dac_src_input_as_needed(void * arg,int direction)95 static mus_float_t dac_src_input_as_needed(void *arg, int direction)
96 {
97   dac_info *dp = (dac_info *)arg;
98   if ((direction != dp->direction) &&
99       (dp->chn_fd))
100     {
101       read_sample_change_direction(dp->chn_fd, (direction == 1) ? READ_FORWARD : READ_BACKWARD);
102       dp->direction = direction;
103     }
104   return((*(dp->dac_sample))(dp));
105 }
106 
107 
speed(dac_info * dp,mus_float_t sr)108 static mus_float_t speed(dac_info *dp, mus_float_t sr)
109 {
110   if (dp->never_sped)
111     return((*(dp->dac_sample))(dp));
112   if (!dp->src)
113     dp->src = mus_make_src(&dac_src_input_as_needed, sr, sinc_width(ss), (void *)dp);
114   mus_set_increment(dp->src, sr);
115   return(mus_src(dp->src, 0.0, &dac_src_input_as_needed));
116 }
117 
118 
119 /* -------- granular synthesis -------- */
120 
expand_input_as_needed(void * arg,int dir)121 static mus_float_t expand_input_as_needed(void *arg, int dir)
122 {
123   spd_info *spd = (spd_info *)arg;
124   dac_info *dp;
125   dp = spd->dp;
126   if (spd->speeding)
127     return(speed(dp, spd->sr));
128   else return((*(dp->dac_sample))(dp));
129 }
130 
131 
max_expand_control_len(snd_info * sp)132 static int max_expand_control_len(snd_info *sp)
133 {
134   if (sp->expand_control_length > .5)
135     return(0);
136   return((int)(snd_srate(sp) * .5));
137 }
138 
139 
make_expand(snd_info * sp,mus_float_t initial_ex,dac_info * dp)140 static void *make_expand(snd_info *sp, mus_float_t initial_ex, dac_info *dp)
141 {
142   spd_info *spd;
143   spd = (spd_info *)calloc(1, sizeof(spd_info));
144   spd->gen = mus_make_granulate(&expand_input_as_needed,
145 				initial_ex, sp->expand_control_length,
146 				.6, /* expand scaler, not currently settable -- dac_set_expand_scaler below */
147 				sp->expand_control_hop, sp->expand_control_ramp,
148 				sp->expand_control_jitter,
149 				max_expand_control_len(sp), NULL, (void *)spd);
150   spd->dp = dp;
151   spd->speeding = false;
152   spd->sr = 0.0;
153   return(spd);
154 }
155 
156 
free_expand(void * ur_spd)157 static void free_expand(void *ur_spd)
158 {
159   spd_info *spd = (spd_info *)ur_spd;
160   if (ur_spd)
161     {
162       mus_free(spd->gen);
163       free(spd);
164     }
165 }
166 
167 
expand(dac_info * dp,mus_float_t sr,mus_float_t ex)168 static mus_float_t expand(dac_info *dp, mus_float_t sr, mus_float_t ex)
169 {
170   /* from mixer.sai, used in "Leviathan", 1986 */
171   bool speeding;
172   snd_info *sp;
173   spd_info *spd;
174   sp = dp->sp;
175   speeding = ((sp->speed_control_direction != 1) || (!(snd_feq(sp->speed_control, 1.0))) || (!(snd_feq(dp->cur_srate, 1.0))));
176   spd = dp->spd;
177   spd->speeding = speeding;
178   spd->sr = sr;
179   return(mus_granulate(spd->gen, &expand_input_as_needed));
180 }
181 
182 
183 /* -------- reverb -------- */
184 
185 /* to implement run-time reverb-length, we would need to save the individual delay lens, both nominal
186    and actual (given srate); set up the combs/allpasses to make room for 5.0 as reverb_control_length,
187    then at each call, check current reverb len (how?), and if not original, recalculate how many
188    samples each delay needs to be expanded by and so on -- more complexity than it's worth!
189 */
190 
prime(int num)191 static int prime(int num)
192 {
193   if (num == 2) return(1);
194   if ((num % 2) == 1)
195     {
196       int lim, i;
197       lim = (int)(sqrt(num));
198       for (i = 3; i < lim; i += 2)
199 	if ((num % i) == 0)
200 	  return(0);
201       return(1);
202     }
203   return(0);
204 }
205 
206 
get_prime(int num)207 static int get_prime(int num)
208 {
209   int i;
210   if ((num % 2) == 1)
211     i = num;
212   else i = num + 1;
213   while (!(prime(i))) i += 2;
214   return(i);
215 }
216 
217 
218 typedef struct {
219   int num_combs;
220   mus_any **combs;
221   int num_allpasses;
222   mus_any **allpasses;
223   mus_any *onep;
224 } rev_info;
225 
226 static rev_info *global_rev = NULL;
227 
nrev(rev_info * r,mus_float_t * rins,mus_float_t * routs,int chans)228 static void nrev(rev_info *r, mus_float_t *rins, mus_float_t *routs, int chans)
229 {
230   mus_float_t rout, rin = 0.0;
231   int i;
232   for (i = 0; i < chans; i++) rin += rins[i];
233   rout = mus_all_pass_unmodulated_noz(r->allpasses[3],
234 	   mus_one_pole(r->onep,
235 	     mus_all_pass_unmodulated_noz(r->allpasses[2],
236 	       mus_all_pass_unmodulated_noz(r->allpasses[1],
237 		 mus_all_pass_unmodulated_noz(r->allpasses[0],
238 	           mus_comb_unmodulated_noz(r->combs[0], rin) +
239 	           mus_comb_unmodulated_noz(r->combs[1], rin) +
240 	           mus_comb_unmodulated_noz(r->combs[2], rin) +
241 	           mus_comb_unmodulated_noz(r->combs[3], rin) +
242 	           mus_comb_unmodulated_noz(r->combs[4], rin) +
243 	           mus_comb_unmodulated_noz(r->combs[5], rin))))));
244   for (i = 0; i < chans; i++)
245     routs[i] = mus_all_pass_unmodulated_noz(r->allpasses[i + 4], rout);
246 }
247 
248 
249 static mus_float_t *r_ins, *r_outs;
250 static int reverb_chans = 0;
251 
reverb(rev_info * r,mus_float_t ** rins,mus_float_t ** outs,int ind)252 static void reverb(rev_info *r, mus_float_t **rins, mus_float_t **outs, int ind)
253 {
254   int i, chans;
255   chans = reverb_chans;
256   for (i = 0; i < chans; i++)
257     {
258       r_ins[i] = rins[i][ind];
259       r_outs[i] = 0.0;
260     }
261   nrev(r, r_ins, r_outs, chans);
262   for (i = 0; i < chans; i++)
263     outs[i][ind] += (r_outs[i]);
264 }
265 
266 
free_reverb(void)267 static void free_reverb(void)
268 {
269   rev_info *r;
270   r = global_rev;
271   if (r)
272     {
273       int i;
274       if (r->combs)
275 	{
276 	  for (i = 0; i < r->num_combs; i++)
277 	    if (r->combs[i])
278 	      mus_free(r->combs[i]);
279 	  free(r->combs);
280 	}
281       if (r->onep) mus_free(r->onep);
282       if (r->allpasses)
283 	{
284 	  for (i = 0; i < r->num_allpasses; i++)
285 	    if (r->allpasses[i])
286 	      mus_free(r->allpasses[i]);
287 	  free(r->allpasses);
288 	}
289       free(r);
290     }
291   global_rev = NULL;
292 }
293 
294 
295 static mus_float_t *comb_factors = NULL;
296 
make_nrev(snd_info * sp,int chans)297 static rev_info *make_nrev(snd_info *sp, int chans)
298 {
299   /* Mike McNabb's nrev from Mus10 days (ca. 1978) */
300   #define BASE_DLY_LEN 14
301   static int base_dly_len[BASE_DLY_LEN] = {1433, 1601, 1867, 2053, 2251, 2399, 347, 113, 37, 59, 43, 37, 29, 19};
302   static int dly_len[BASE_DLY_LEN];
303   #define NREV_COMBS 6
304   static mus_float_t nrev_comb_factors[NREV_COMBS] = {0.822, 0.802, 0.773, 0.753, 0.753, 0.733};
305   mus_float_t srscale;
306   int i, j, len;
307   rev_info *r;
308 
309   if (!sp) return(NULL);
310 
311   srscale = sp->reverb_control_length * snd_srate(sp) / 25641.0;
312   for (i = 0; i < BASE_DLY_LEN; i++)
313     dly_len[i] = get_prime((int)(srscale * base_dly_len[i]));
314   r = (rev_info *)calloc(1, sizeof(rev_info));
315   r->num_combs = NREV_COMBS;
316   r->combs = (mus_any **)calloc(r->num_combs, sizeof(mus_any *));
317   r->num_allpasses = 4 + chans;
318   r->allpasses = (mus_any **)calloc(r->num_allpasses, sizeof(mus_any *));
319 
320   if (comb_factors) free(comb_factors);
321   comb_factors = (mus_float_t *)calloc(r->num_combs, sizeof(mus_float_t));
322   for (i = 0; i < r->num_combs; i++)
323     {
324       comb_factors[i] = nrev_comb_factors[i];
325       r->combs[i] = mus_make_comb(comb_factors[i] * sp->reverb_control_feedback, dly_len[i], NULL, dly_len[i], MUS_INTERP_LINEAR);
326     }
327 
328   r->onep = mus_make_one_pole(sp->reverb_control_lowpass, sp->reverb_control_lowpass - 1.0);
329 
330   for (i = 0, j = r->num_combs; i < 4; i++, j++)
331     r->allpasses[i] = mus_make_all_pass(-0.700, 0.700, dly_len[j], NULL, dly_len[j], MUS_INTERP_LINEAR);
332 
333   for (i = 0, j = 10; i < chans; i++)
334     {
335       if (j < BASE_DLY_LEN)
336 	len = dly_len[j];
337       else len = get_prime((int)(40 + mus_random(20.0)));
338       r->allpasses[i + 4] = mus_make_all_pass(-0.700, 0.700, len, NULL, len, MUS_INTERP_LINEAR);
339     }
340 
341   return(r);
342 }
343 
344 
make_reverb(snd_info * sp,int chans)345 static void make_reverb(snd_info *sp, int chans)
346 {
347   reverb_chans = chans;
348   global_rev = make_nrev(sp, chans);
349 }
350 
351 
set_reverb_filter_coeff(mus_float_t newval)352 static void set_reverb_filter_coeff(mus_float_t newval)
353 {
354   if (global_rev)
355     {
356       mus_set_xcoeff(global_rev->onep, 0, newval);
357       mus_set_ycoeff(global_rev->onep, 1, 1.0 - newval);
358     }
359 }
360 
361 
set_reverb_comb_factors(mus_float_t val)362 static void set_reverb_comb_factors(mus_float_t val)
363 {
364   int j;
365   if (global_rev)
366     for (j = 0; j < global_rev->num_combs; j++)
367       mus_set_scaler(global_rev->combs[j], comb_factors[j] * val);
368 }
369 
370 
371 /* -------- contrast-enhancement -------- */
372 
contrast(dac_info * dp,mus_float_t amp,mus_float_t index,mus_float_t inval)373 static mus_float_t contrast (dac_info *dp, mus_float_t amp, mus_float_t index, mus_float_t inval)
374 {
375   return(amp * mus_contrast_enhancement(dp->contrast_amp * inval, index));
376 }
377 
378 
379 
380 static mus_error_handler_t *old_error_handler;
381 static bool got_local_error = false;
382 
local_mus_error(int type,char * msg)383 static void local_mus_error(int type, char *msg)
384 {
385   got_local_error = true;
386   snd_error_without_format(msg);
387 }
388 
389 
sample_linear_env(env * e,int order)390 mus_float_t *sample_linear_env(env *e, int order)
391 {
392   /* used only for filter coeffs (env here is the frequency response curve) */
393   mus_any *ge;
394   int ordp;
395   mus_float_t *data = NULL;
396   mus_float_t last_x, step;
397 
398   last_x = e->data[(e->pts - 1) * 2];
399   step = 2 * last_x / ((mus_float_t)order - 1);
400   ordp = e->pts;
401   if (ordp < order) ordp = order;
402 
403   got_local_error = false;
404   old_error_handler = mus_error_set_handler(local_mus_error);
405   ge = mus_make_env(e->data, e->pts, 1.0, 0.0, e->base, 0.0, 1000 * ordp, NULL);
406   mus_error_set_handler(old_error_handler);
407 
408   if (!got_local_error)
409     {
410       int i, j;
411       mus_float_t x;
412       data = (mus_float_t *)malloc(order * sizeof(mus_float_t));
413       for (i = 0, x = 0.0; i < order / 2; i++, x += step)
414 	data[i] = mus_env_interp(x, ge);
415       for (j = order / 2 - 1, i = order / 2; (i < order) && (j >= 0); i++, j--)
416 	data[i] = data[j];
417       mus_free(ge);
418     }
419 
420   return(data);
421 }
422 
423 
free_dac_info(dac_info * dp,play_stop_t reason)424 static void free_dac_info(dac_info *dp, play_stop_t reason)
425 {
426   bool looping;
427   looping = (dp->stop_procedure == Xen_true);
428   if (dp->stop_procedure_gc_loc != NOT_A_GC_LOC)
429     {
430       Xen_call_with_1_arg(dp->stop_procedure,
431 			  C_int_to_Xen_integer((int)reason),
432 			  "play stop procedure");
433       snd_unprotect_at(dp->stop_procedure_gc_loc);
434       dp->stop_procedure = Xen_false;
435       dp->stop_procedure_gc_loc = NOT_A_GC_LOC;
436     }
437   if (dp->func_gc_loc != NOT_A_GC_LOC)
438     {
439       snd_unprotect_at(dp->func_gc_loc);
440       dp->func = Xen_false;
441       dp->func_gc_loc = NOT_A_GC_LOC;
442     }
443   if (dp->a) {free(dp->a); dp->a = NULL; dp->a_size = 0;}
444   dp->chn_fd = free_snd_fd(dp->chn_fd);
445   if (dp->spd) free_expand(dp->spd);
446   if (dp->src) mus_free(dp->src);
447   if (dp->flt) mus_free(dp->flt);
448   dp->sp = NULL;
449   dp->cp = NULL;
450   dp->type = DAC_NOTHING;
451   free(dp);
452   if ((looping) &&
453       (reason == PLAY_COMPLETE))
454     loop_play_selection();
455 }
456 
457 
458 static int dac_max_sounds = 0;
459 static dac_info **play_list = NULL;
460 #define INITIAL_MAX_SOUNDS 16
461 static int play_list_members = 0;
462 static int max_active_slot = -1;
463 
464 
465 /* -------------------------------- special control panel variables -------------------------------- */
466 
467 typedef enum {DAC_EXPAND, DAC_EXPAND_RAMP, DAC_EXPAND_LENGTH, DAC_EXPAND_HOP, DAC_EXPAND_SCALER,
468 	      DAC_CONTRAST_AMP, DAC_REVERB_FEEDBACK, DAC_REVERB_LOWPASS} dac_field_t;
469 
dac_set_field(snd_info * sp,mus_float_t newval,dac_field_t field)470 static void dac_set_field(snd_info *sp, mus_float_t newval, dac_field_t field)
471 {
472   /* if sp == NULL, sets globally */
473   if (play_list)
474     {
475       if (field == DAC_REVERB_LOWPASS)
476 	{
477 	  if (global_rev)
478 	    set_reverb_filter_coeff(newval);
479 	}
480       else
481 	{
482 	  if (field == DAC_REVERB_FEEDBACK)
483 	    {
484 	      if (global_rev)
485 		set_reverb_comb_factors(newval);
486 	    }
487 	  else
488 	    {
489 	      int i;
490 	      for (i = 0; i <= max_active_slot; i++)
491 		{
492 		  dac_info *dp;
493 		  dp = play_list[i];
494 		  if ((dp) && ((!sp) || (sp == dp->sp)))
495 		    {
496 		      int val;
497 		      switch (field)
498 			{
499 			case DAC_EXPAND:
500 			  if (dp->spd)
501 			    mus_set_increment(dp->spd->gen, newval);
502 			  break;
503 
504 			case DAC_EXPAND_LENGTH: /* segment length */
505 			  if (dp->spd)
506 			    {
507 			      val = (int)(snd_srate(sp) * newval);
508 			      mus_set_length(dp->spd->gen, val);
509 			      mus_set_ramp(dp->spd->gen, (int)(val * sp->expand_control_ramp));
510 			    }
511 			  break;
512 
513 			case DAC_EXPAND_RAMP:
514 			  if (dp->spd)
515 			    {
516 			      val = (int)(newval * sp->expand_control_length * snd_srate(sp));
517 			      mus_set_ramp(dp->spd->gen, val);
518 			    }
519 			  break;
520 
521 			case DAC_EXPAND_HOP: /* output hop */
522 			  if (dp->spd)
523 			    {
524 			      val = (int)(snd_srate(sp) * newval);
525 			      mus_set_hop(dp->spd->gen, val);
526 			      mus_set_increment(dp->spd->gen, sp->expand_control);
527 			    }
528 			  break;
529 
530 			case DAC_EXPAND_SCALER:
531 			  if (dp->spd)
532 			    mus_set_scaler(dp->spd->gen, newval);
533 			  break;
534 
535 			case DAC_CONTRAST_AMP:
536 			  dp->contrast_amp = newval;
537 			  break;
538 
539 			default:
540 			  break;
541 			}
542 		    }
543 		}
544 	    }
545 	}
546     }
547 }
548 
549 
dac_set_expand(snd_info * sp,mus_float_t newval)550 void dac_set_expand(snd_info *sp, mus_float_t newval) {dac_set_field(sp, newval, DAC_EXPAND);}
dac_set_expand_length(snd_info * sp,mus_float_t newval)551 void dac_set_expand_length(snd_info *sp, mus_float_t newval) {dac_set_field(sp, newval, DAC_EXPAND_LENGTH);}
dac_set_expand_ramp(snd_info * sp,mus_float_t newval)552 void dac_set_expand_ramp(snd_info *sp, mus_float_t newval) {dac_set_field(sp, newval, DAC_EXPAND_RAMP);}
dac_set_expand_hop(snd_info * sp,mus_float_t newval)553 void dac_set_expand_hop(snd_info *sp, mus_float_t newval) {dac_set_field(sp, newval, DAC_EXPAND_HOP);}
554 /* static void dac_set_expand_scaler(snd_info *sp, mus_float_t newval) {dac_set_field(sp, newval, DAC_EXPAND_SCALER);} */ /* not currently accessible */
dac_set_contrast_amp(snd_info * sp,mus_float_t newval)555 void dac_set_contrast_amp(snd_info *sp, mus_float_t newval) {dac_set_field(sp, newval, DAC_CONTRAST_AMP);}
dac_set_reverb_feedback(snd_info * sp,mus_float_t newval)556 void dac_set_reverb_feedback(snd_info *sp, mus_float_t newval) {dac_set_field(sp, newval, DAC_REVERB_FEEDBACK);}
dac_set_reverb_lowpass(snd_info * sp,mus_float_t newval)557 void dac_set_reverb_lowpass(snd_info *sp, mus_float_t newval) {dac_set_field(sp, newval, DAC_REVERB_LOWPASS);}
558 
559 
560 
561 /* -------------------------------- stop playing (remove from play-list) -------------------------------- */
562 
563 typedef struct {
564   int srate;                   /* output srate */
565   int channels;                /* total output channels currently active */
566   int framples;                /* samples per channel per output block */
567   int devices;                 /* output devices active */
568   int *chans_per_device;       /* channels sent to each active device */
569   mus_sample_t out_samp_type;  /* output sample type */
570   int slice;                   /* background process state (i.e. starting, running, quitting) */
571   mus_long_t reverb_ring_framples; /* how long the reverb rings after the end (if reverb, of course) */
572 
573 } dac_state;
574 
575 static dac_state *snd_dacp = NULL;
576 
free_dac_state(void)577 static void free_dac_state(void)
578 {
579   if (snd_dacp)
580     {
581       if (snd_dacp->chans_per_device) free(snd_dacp->chans_per_device);
582       free(snd_dacp);
583       snd_dacp = NULL;
584     }
585 }
586 
587 static bool dac_running = false;
588 static Xen play_hook;
589 static Xen start_playing_hook;
590 static Xen stop_playing_hook;
591 static Xen stop_playing_selection_hook;
592 static Xen start_playing_selection_hook;
593 
594 
595 #define MAX_DEVICES 8
596 static int dev_fd[MAX_DEVICES] = {-1, -1, -1, -1, -1, -1, -1, -1};
597 
cleanup_dac(void)598 void cleanup_dac(void)
599 {
600   /* called only from snd_exit_cleanly in snd-main.c */
601   int i;
602   if (dac_running)
603     for (i = 0; i < MAX_DEVICES; i++)
604       if (dev_fd[i] != -1)
605 	{
606 	  mus_audio_close(dev_fd[i]);
607 	  dev_fd[i] = -1;
608 	}
609   for (i = 0; i < MAX_DEVICES; i++) dev_fd[i] = -1;
610   dac_running = false;
611 }
612 
613 
something_is_playing(void)614 static bool something_is_playing(void)
615 {
616   int i;
617   if (play_list)
618     for (i = 0; i < dac_max_sounds; i++)
619       if (play_list[i]) return(true);
620   return(false);
621 }
622 
623 
reflect_play_stop(snd_info * sp)624 static void reflect_play_stop(snd_info *sp)
625 {
626 #if (!USE_NO_GUI)
627   set_control_panel_play_button(sp);
628 #endif
629   set_open_file_play_button(false);
630 #if (!USE_NO_GUI)
631   view_files_unplay();
632 #endif
633   if (!something_is_playing())
634     ss->tracking = false;
635 }
636 
637 
638 static void free_player_sound(snd_info *sp);
639 
640 typedef enum {WITHOUT_TOGGLE, WITH_TOGGLE} dac_toggle_t;
641 
stop_playing_with_toggle(dac_info * dp,dac_toggle_t toggle,with_hook_t with_hook,play_stop_t reason)642 static void stop_playing_with_toggle(dac_info *dp, dac_toggle_t toggle, with_hook_t with_hook, play_stop_t reason)
643 {
644   snd_info *sp = NULL;
645   bool sp_stopping = false;
646 
647   if ((!dp) || (!play_list)) return;
648   sp = dp->sp;
649   if ((sp) && (sp->inuse != SOUND_IDLE))
650     {
651       if (sp->playing > 0) sp->playing--;
652       if (sp->playing == 0) sp_stopping = true;
653 
654       if ((sp_stopping) &&
655 	  (ss->tracking) &&
656 	  (sp->inuse == SOUND_NORMAL) &&
657 	  (sp->index >= 0))
658 	{
659 	  uint32_t i;
660 	  for (i = 0; i < sp->nchans; i++)
661 	    {
662 	      chan_info *cp;
663 	      cp = sp->chans[i];
664 	      if ((cp == dp->cp) ||
665 		  (sp->sync != 0))        /* don't move the cursor in a channel that isn't playing */
666 		{
667 		  if (with_tracking_cursor(ss) == TRACK_AND_STAY)
668 		    {
669 		      if (dp->cur_srate > 0.0)
670 			{
671 			  mus_long_t samp;
672 			  samp = current_location(dp->chn_fd) + ((snd_dacp) ? snd_dacp->framples : 0);
673 			  if (dp->selection)
674 			    {
675 			      if (samp > selection_end(cp))
676 				samp = selection_end(cp);
677 			    }
678 			  else
679 			    {
680 			      if (samp > current_samples(cp))
681 				samp = current_samples(cp);
682 			    }
683 			  cursor_sample(cp) = samp - 1;
684 			}
685 		      cp->original_left_sample = cursor_sample(cp) - (cp->original_window_size / 2);
686 		      if (cp->original_left_sample < 0)
687 			cp->original_left_sample = 0;
688 		    }
689 		  else cursor_sample(cp) = cp->original_cursor;
690 		  cursor_moveto_with_window(cp, cursor_sample(cp), cp->original_left_sample, cp->original_window_size);
691 		  update_graph(cp);
692 		}
693 	    }
694 	}
695       /* if ctrl-click play, c-t, c-q -> this flag is still set from aborted previous play, so clear at c-t (or c-g) */
696     }
697   else sp = NULL; /* don't free it as a player below */
698   play_list[dp->slot] = NULL;
699   play_list_members--;
700 
701   if (toggle == WITH_TOGGLE)
702     {
703       switch (dp->type)
704 	{
705 	case DAC_CHANNEL:
706 	  if ((sp) && (sp->playing <= 0))
707 	    reflect_play_stop(sp);
708 	  break;
709 	case DAC_REGION:
710 	  if (dp->region != INVALID_REGION)
711 	    reflect_play_region_stop(dp->region);
712 	  break;
713 	case DAC_MIX:
714 	  if (dp->mix_id != INVALID_MIX_ID)
715 	    reflect_mix_play_stop();
716 	case DAC_XEN:
717 	  break;
718 	case DAC_NOTHING:
719 	  break;
720 	}
721     }
722 
723   if (dp->slot == max_active_slot) max_active_slot--;
724   if (with_hook == WITH_HOOK)
725     {
726       if (dp->type == DAC_CHANNEL)
727 	{
728 	  if (dp->selection)
729 	    {
730 	      reflect_play_selection_stop();
731 	      if (Xen_hook_has_list(stop_playing_selection_hook))
732 		run_hook(stop_playing_selection_hook,
733 			 Xen_empty_list,
734 			 S_stop_playing_selection_hook);
735 	    }
736 	  else
737 	    {
738 	      if ((sp_stopping) && (Xen_hook_has_list(stop_playing_hook)))
739 		run_hook(stop_playing_hook,
740 			 Xen_list_1(C_int_to_Xen_sound(sp->index)),
741 			 S_stop_playing_hook);
742 	    }
743 	}
744     }
745 
746   if ((sp) && (is_player_sound(sp)))
747     {
748       int k;
749       bool free_ok = true;
750       dp->sp = NULL; /* free_player_sound frees it */
751       for (k = dp->slot + 1; k < dac_max_sounds; k++)
752 	{
753 	  dac_info *ndp;
754 	  ndp = play_list[k];
755 	  if ((ndp) &&
756 	      (ndp->sp) &&
757 	      (ndp->sp == sp))
758 	    {
759 	      free_ok = false;
760 	      break;
761 	    }
762 	}
763       if (free_ok)
764 	free_player_sound(sp);
765       sp = NULL;
766     }
767   free_dac_info(dp, reason); /* this will call the stop-function, if any */
768 
769   if ((sp) && (sp_stopping) && (sp->delete_me))
770     {
771 #if USE_MOTIF
772       if (sp->delete_me != (void *)1)
773 	clear_deleted_snd_info(sp->delete_me); /* for various file dialog play buttons */
774 #endif
775       completely_free_snd_info(sp); /* dummy snd_info struct for (play "filename") in snd-xen.c */
776     }
777 }
778 
779 
stop_playing(dac_info * dp,with_hook_t with_hook,play_stop_t reason)780 static void stop_playing(dac_info *dp, with_hook_t with_hook, play_stop_t reason)
781 {
782   stop_playing_with_toggle(dp, WITH_TOGGLE, with_hook, reason);
783 }
784 
785 
786 static idle_func_t dac_in_background(any_pointer_t ptr);
787 static idle_func_t dac_work_proc = 0;
788 
789 
stop_playing_sound_with_toggle(snd_info * sp,dac_toggle_t toggle,with_hook_t with_hook,play_stop_t reason)790 static void stop_playing_sound_with_toggle(snd_info *sp, dac_toggle_t toggle, with_hook_t with_hook, play_stop_t reason)
791 {
792   /* this needs to scan all current play_list members and remove any that are referring
793    * to sp, even indirectly (as through the current selection)
794    */
795 
796   /* if reason = PLAY_STOP_CALLED, PLAY_C_T, or PLAY_C_G, we need to slam the output volumes to 0.0 (so the
797    *    draining output is not played), then once we're sure output has stopped, put them back where they were.
798    *    But, this action is sound-driver (OS) specific -- perhaps just hit MUS_AUDIO_AMP and hope?
799    *    How to tell that on-card buffers are finally empty?  Wait 1 or 2 seconds?
800    *
801    * an added annoyance -- to get current amps, we have to make separate read call for each chan!
802    */
803 
804   if ((sp) && (play_list))
805     {
806       int i;
807       dac_info *dp;
808       for (i = 0; i < dac_max_sounds; i++)
809 	if ((play_list[i]) &&
810 	    (sp == (play_list[i]->sp)))
811 	  {
812 	    dp = play_list[i];
813 	    play_list[i] = NULL;
814 	    stop_playing_with_toggle(dp, toggle, with_hook, reason);
815 	  }
816       if ((play_list_members > 0) && (with_hook == WITH_HOOK))
817 	{
818 	  /* see comment below */
819 	  for (i = 0; i < dac_max_sounds; i++)
820 	    if ((play_list[i]) &&
821 		(sp == (play_list[i]->sp)))
822 	      {
823 		dp = play_list[i];
824 		play_list[i] = NULL;
825 		stop_playing_with_toggle(dp, toggle, WITHOUT_HOOK, reason); /* do it again but with hook disabled */
826 	      }
827 	}
828     }
829 }
830 
831 
stop_playing_sound(snd_info * sp,play_stop_t reason)832 void stop_playing_sound(snd_info *sp, play_stop_t reason)
833 {
834   stop_playing_sound_with_toggle(sp, WITH_TOGGLE, WITH_HOOK, reason);
835 }
836 
837 
stop_playing_sound_without_hook(snd_info * sp,play_stop_t reason)838 void stop_playing_sound_without_hook(snd_info *sp, play_stop_t reason)
839 {
840   stop_playing_sound_with_toggle(sp, WITH_TOGGLE, WITHOUT_HOOK, reason);
841 }
842 
843 
stop_playing_sound_no_toggle(snd_info * sp,play_stop_t reason)844 void stop_playing_sound_no_toggle(snd_info *sp, play_stop_t reason)
845 {
846   stop_playing_sound_with_toggle(sp, WITHOUT_TOGGLE, WITH_HOOK, reason);
847 }
848 
849 
stop_playing_all_sounds_1(with_hook_t with_hook,play_stop_t reason)850 static void stop_playing_all_sounds_1(with_hook_t with_hook, play_stop_t reason)
851 {
852   if (play_list)
853     {
854       int i;
855       dac_info *dp;
856       for (i = 0; i < dac_max_sounds; i++)
857 	{
858 	  dp = play_list[i];
859 	  play_list[i] = NULL;
860 	  stop_playing(dp, with_hook, reason);
861 	  /* this can call stop_playing_hook which can make a new play_list member where the old one used to be! */
862 	}
863       if ((play_list_members > 0) && (with_hook == WITH_HOOK))
864 	{
865 	  /* stop-playing-hook must have started a new player, but in this case,
866 	   *   the rest of Snd assumes the dac has stopped no matter what.  So,
867 	   *   we make a second pass with the hooks disabled.
868 	   */
869 	  for (i = 0; i < dac_max_sounds; i++)
870 	    {
871 	      dp = play_list[i];
872 	      play_list[i] = NULL;
873 	      stop_playing(dp, WITHOUT_HOOK, reason);
874 	    }
875 	}
876     }
877   if (snd_dacp)
878     {
879       snd_dacp->slice = 2;
880       dac_in_background(NULL);   /* try to force completion (work proc might not be called above) */
881     }
882 }
883 
884 
stop_playing_all_sounds(play_stop_t reason)885 void stop_playing_all_sounds(play_stop_t reason)
886 {
887   stop_playing_all_sounds_1(WITH_HOOK, reason);
888 }
889 
890 
stop_playing_all_sounds_without_hook(play_stop_t reason)891 static void stop_playing_all_sounds_without_hook(play_stop_t reason)
892 {
893   stop_playing_all_sounds_1(WITHOUT_HOOK, reason);
894 }
895 
896 
stop_playing_region(int n,play_stop_t reason)897 void stop_playing_region(int n, play_stop_t reason)
898 {
899   if (play_list)
900     {
901       int i;
902       dac_info *dp;
903       for (i = 0; i < dac_max_sounds; i++)
904 	if ((play_list[i]) &&
905 	    (play_list[i]->type == DAC_REGION) &&
906 	    (play_list[i]->region == n))
907 	  {
908 	    dp = play_list[i];
909 	    play_list[i] = NULL;
910 	    stop_playing(dp, WITHOUT_HOOK, reason);
911 	  }
912     }
913 }
914 
915 
916 /* -------------------------------- play (add to play-list) -------------------------------- */
917 
find_slot_to_play(void)918 static int find_slot_to_play(void)
919 {
920   int i, old_size;
921 
922   if (!play_list)
923     {
924       dac_max_sounds = INITIAL_MAX_SOUNDS;
925       play_list = (dac_info **)calloc(dac_max_sounds, sizeof(dac_info *));
926     }
927 
928   for (i = 0; i < dac_max_sounds; i++)
929     if (!play_list[i])
930       return(i);
931 
932   old_size = dac_max_sounds;
933   dac_max_sounds += INITIAL_MAX_SOUNDS;
934   play_list = (dac_info **)realloc(play_list, dac_max_sounds * sizeof(dac_info *));
935   for (i = old_size; i < dac_max_sounds; i++) play_list[i] = NULL;
936 
937   return(old_size);
938 }
939 
940 
make_dac_info(int slot,chan_info * cp,snd_info * sp,snd_fd * fd,mus_long_t end,int out_chan,dac_source_t type,Xen func)941 static dac_info *make_dac_info(int slot, chan_info *cp, snd_info *sp, snd_fd *fd, mus_long_t end, int out_chan, dac_source_t type, Xen func)
942 {
943   dac_info *dp;
944 
945   dp = (dac_info *)calloc(1, sizeof(dac_info)); /* only place dac_info is created */
946   dp->stop_procedure = Xen_false;
947   dp->stop_procedure_gc_loc = NOT_A_GC_LOC;
948   dp->region = INVALID_REGION;
949   dp->mix_id = INVALID_MIX_ID;
950   dp->direction = 1;
951   dp->type = type; /* dac needs to know how to get input before calling mus_make_src below */
952   if (type == DAC_NOTHING)
953     dp->dac_sample = dac_no_sample;
954   else
955     {
956       if (type == DAC_XEN)
957 	{
958 	  dp->dac_sample = dac_xen_sample;
959 	  dp->func = func;
960 	  dp->func_gc_loc = snd_protect(func);
961 	}
962       else
963 	{
964 	  dp->dac_sample = dac_read_sample;
965 	  dp->func = Xen_false;
966 	  dp->func_gc_loc = NOT_A_GC_LOC;
967 	}
968     }
969   dp->a = NULL;
970   dp->a_size = 0;
971   dp->audio_chan = out_chan;
972   dp->never_sped = true;
973   dp->chn_fd = fd;
974   dp->sp = sp;
975   dp->cp = cp;
976 
977   /* next block may get input */
978   if (sp)
979     {
980       dp->expanding = sp->expand_control_on;
981       dp->filtering = ((sp->filter_control_on) && (sp->filter_control_order > 0));
982       dp->reverbing = sp->reverb_control_on;
983       dp->contrast_amp = sp->contrast_control_amp;
984 
985       if ((!(snd_feq(sp->speed_control, 1.0))) ||
986 	  (sp->speed_control_direction == -1))
987 	{
988 	  dp->src = mus_make_src(&dac_src_input_as_needed, sp->speed_control, sinc_width(ss), (void *)dp);
989 	  dp->direction = sp->speed_control_direction;
990 	}
991 
992       if (dp->expanding)
993 	{
994 	  dp->spd = (spd_info *)make_expand(sp, sp->expand_control, dp);
995 	  dp->expand_ring_framples = (int)(snd_srate(sp) * sp->expand_control * sp->expand_control_length * 2);
996 	}
997 
998       if (dp->filtering)
999 	{
1000 	  sp->filter_control_changed = false;
1001 	  if (!(sp->filter_control_envelope))
1002 	    dp->filtering = false;
1003 	  else
1004 	    {
1005 	      mus_float_t *data;
1006 	      data = sample_linear_env(sp->filter_control_envelope, sp->filter_control_order);
1007 	      if (data)
1008 		{
1009 		  dp->flt = make_flt(dp, sp->filter_control_order, data);
1010 		  free(data);
1011 		}
1012 	      else dp->filtering = false;
1013 	    }
1014 	}
1015     }
1016 
1017   play_list_members++;
1018   dp->end = end;
1019   play_list[slot] = dp;
1020   dp->slot = slot;
1021   if (max_active_slot < slot) max_active_slot = slot;
1022   if (sp)
1023     {
1024       dp->cur_srate = sp->speed_control * sp->speed_control_direction;
1025       if (!(snd_feq(dp->cur_srate, 1.0))) dp->never_sped = false;
1026       dp->cur_amp = AMP_CONTROL(sp, dp);
1027       dp->cur_index = sp->contrast_control;
1028       dp->cur_exp = sp->expand_control;
1029       dp->cur_rev = sp->reverb_control_scale;
1030     }
1031 
1032   return(dp);
1033 }
1034 
1035 
start_dac(int srate,int channels,play_process_t background,mus_float_t decay)1036 static void start_dac(int srate, int channels, play_process_t background, mus_float_t decay)
1037 {
1038   int i;
1039   /* look for channel folding cases etc */
1040   /* channels = how many output audio chans we have; dac_combines_channels sets whether to wrap or muffle chans outside this limit */
1041 
1042   if (with_tracking_cursor(ss) != DONT_TRACK)
1043     ss->tracking = true;
1044 
1045   for (i = 0; i <= max_active_slot; i++)
1046     {
1047       dac_info *dp;
1048       dp = play_list[i];
1049       if ((dp) && (dac_running))                          /* dac_running also if apply */
1050 	{
1051 	  /* in the "normal" (non-apply) case the reverb allocation is deferred until we're sure about the number of output channels */
1052 	  if ((dp->reverbing) &&
1053 	      (dp->sp) &&
1054 	      (!global_rev))
1055 	    make_reverb(dp->sp, channels);
1056 	  if (snd_dacp)
1057 	    {
1058 	      if (dp->audio_chan >= snd_dacp->channels)      /* if dac_running, the number of channels has already been set and won't change */
1059 		{
1060 		  if (dac_combines_channels(ss))
1061 		    dp->audio_chan %= snd_dacp->channels;
1062 		  else stop_playing(dp, WITHOUT_HOOK, PLAY_NO_CHANNEL);
1063 		}
1064 	    }
1065 	}
1066     }
1067   /* any number of sounds can be piling into the dac buffers with new ones added at any time
1068    *   (this is the play_list -- an array of dac_info pointers, each one a separate channel rendition)
1069    */
1070   if (!dac_running)
1071     {
1072       if (snd_dacp) free_dac_state();
1073       snd_dacp = (dac_state *)calloc(1, sizeof(dac_state));
1074       snd_dacp->slice = 0;
1075       snd_dacp->srate = srate;
1076       snd_dacp->out_samp_type = MUS_AUDIO_COMPATIBLE_SAMPLE_TYPE;
1077       if (snd_dacp->srate <= 0) snd_dacp->srate = 44100;
1078       snd_dacp->channels = channels;
1079       if (dac_size(ss) > 0)
1080 	snd_dacp->framples = dac_size(ss);
1081       else snd_dacp->framples = 256;
1082       snd_dacp->devices = 1;  /* just a first guess */
1083       snd_dacp->reverb_ring_framples = (mus_long_t)(srate * decay);
1084       if (background == IN_BACKGROUND)
1085 	dac_work_proc = BACKGROUND_ADD(dac_in_background, NULL);
1086       else
1087 	{
1088 	  /* here we want to play as an atomic (not background) action */
1089 	  while (dac_in_background(NULL) == BACKGROUND_CONTINUE)
1090 	    check_for_event(); /* need to be able to C-g out of this */
1091 	}
1092     }
1093 }
1094 
1095 
1096 /* pos = to_c_edit_position(cp, edpos, caller, arg_pos); */
1097 
add_channel_to_play_list(chan_info * cp,snd_info * sp,mus_long_t start,mus_long_t end,int pos,int out_chan)1098 static dac_info *add_channel_to_play_list(chan_info *cp, snd_info *sp, mus_long_t start, mus_long_t end, int pos, int out_chan)
1099 {
1100   /* if not sp, control panel is ignored */
1101   int slot;
1102   read_direction_t direction = READ_FORWARD;
1103   mus_long_t beg = 0;
1104   snd_fd *sf;
1105 
1106   if (pos == AT_CURRENT_EDIT_POSITION) pos = cp->edit_ctr;
1107   if (sp)
1108     {
1109       if (sp->speed_control_direction == 1)
1110 	{
1111 	  if (start >= cp->edits[pos]->samples) return(NULL);
1112 	  direction = READ_FORWARD;
1113 	  beg = start;
1114 	}
1115       else
1116 	{
1117 	  direction = READ_BACKWARD;
1118 	  if (start < end)
1119 	    {
1120 	      mus_long_t tmp;
1121 	      tmp = start;
1122 	      start = end;
1123 	      end = tmp;
1124 	    }
1125 	  if ((start == 0) || (start >= cp->edits[pos]->samples)) /* don't be pedantic about the start point */
1126 	    beg = cp->edits[pos]->samples - 1;
1127 	  else beg = start;
1128 	  if ((end == 0) || (end >= (cp->edits[pos]->samples)))
1129 	    end = NO_END_SPECIFIED;
1130 	}
1131     }
1132 
1133   slot = find_slot_to_play();
1134   if (slot == -1) return(NULL);
1135 
1136   sf = init_sample_read_any(beg, cp, direction, pos);
1137   if (sf)
1138     {
1139       if (sp)
1140 	{
1141 	  sp->playing++;
1142 	  if (((with_tracking_cursor(ss) != DONT_TRACK) || (ss->tracking)) &&
1143               (!(is_player_sound(sp))) &&
1144 	      (sp->inuse == SOUND_NORMAL))
1145 	    {
1146 	      cp->original_cursor = cursor_sample(cp);
1147 	      if (cp->axis)
1148 		{
1149 		  cp->original_left_sample = cp->axis->losamp;
1150 		  cp->original_window_size = cp->axis->hisamp - cp->axis->losamp;
1151 		}
1152 	      cp->cursor_on = true;
1153 	      cursor_moveto_without_verbosity(cp, start);
1154 #if USE_MOTIF
1155 	      if (cp->widgets)
1156 		update_graph(cp);
1157 #endif
1158 	    }
1159 	}
1160       return(make_dac_info(slot, cp, sp, sf, end, out_chan, DAC_CHANNEL, Xen_false));
1161     }
1162   return(NULL);
1163 }
1164 
1165 
add_region_channel_to_play_list(int region,int chan,mus_long_t beg,mus_long_t end,int out_chan)1166 static dac_info *add_region_channel_to_play_list(int region, int chan, mus_long_t beg, mus_long_t end, int out_chan)
1167 {
1168   int slot;
1169   snd_fd *fd;
1170 
1171   slot = find_slot_to_play();
1172   if (slot == -1) return(NULL);
1173 
1174   fd = init_region_read(beg, region, chan, READ_FORWARD);
1175   if (fd)
1176     {
1177       dac_info *dp;
1178       dp = make_dac_info(slot, fd->cp, NULL, fd, end, out_chan, DAC_REGION, Xen_false);
1179       if (dp) dp->region = region;
1180       return(dp);
1181     }
1182   return(NULL);
1183 }
1184 
1185 
play_region_1(int region,play_process_t background,Xen stop_proc)1186 void play_region_1(int region, play_process_t background, Xen stop_proc)
1187 {
1188   /* just plays region (not current selection) -- no control panel etc */
1189   int chans, i;
1190   dac_info *dp = NULL, *rtn_dp = NULL;
1191 
1192   if ((background == NOT_IN_BACKGROUND) && (play_list_members > 0)) return;
1193   if (!(region_ok(region))) return;
1194   chans = region_chans(region);
1195   if (chans == 0) return;
1196 
1197   for (i = 0; i < chans; i++)
1198     {
1199       dp = add_region_channel_to_play_list(region, i, 0, NO_END_SPECIFIED, i); /* i = out chan */
1200       if (dp)
1201 	{
1202 	  if (!rtn_dp) rtn_dp = dp;
1203 	}
1204     }
1205   if (rtn_dp)
1206     {
1207       rtn_dp->stop_procedure = stop_proc;
1208       if (Xen_is_procedure(stop_proc))
1209 	rtn_dp->stop_procedure_gc_loc = snd_protect(stop_proc);
1210       start_dac(region_srate(region), chans, background, DEFAULT_REVERB_CONTROL_DECAY);
1211     }
1212 }
1213 
1214 
play_region(int region,play_process_t background)1215 void play_region(int region, play_process_t background)
1216 {
1217   play_region_1(region, background, Xen_false);
1218 }
1219 
1220 
add_mix_to_play_list(mix_state * ms,chan_info * cp,mus_long_t beg_within_mix,bool start_playing)1221 bool add_mix_to_play_list(mix_state *ms, chan_info *cp, mus_long_t beg_within_mix, bool start_playing)
1222 {
1223   int slot;
1224   slot = find_slot_to_play();
1225   if (slot != -1)
1226     {
1227       snd_fd *fd;
1228       fd = make_virtual_mix_reader(cp, beg_within_mix, ms->len - beg_within_mix, ms->index, 1.0, READ_FORWARD);
1229       if (fd)
1230 	{
1231 	  dac_info *dp;
1232 	  dp = make_dac_info(slot, cp, NULL, fd, NO_END_SPECIFIED, 0, DAC_MIX, Xen_false);
1233 	  if (dp)
1234 	    {
1235 	      dp->mix_id = ms->index; /* any valid mix id will do */
1236 	      if (start_playing)
1237 		start_dac(snd_srate(cp->sound), 1, NOT_IN_BACKGROUND, DEFAULT_REVERB_CONTROL_DECAY);
1238 	      return(true);
1239 	    }
1240 	}
1241       else free_snd_fd(fd);
1242     }
1243   return(false);
1244 }
1245 
1246 
1247 /* (play (let ((osc (make-oscil 440.0))
1248                (samp 0))
1249            (lambda ()
1250              (if (> samp 20000)
1251 		 #f
1252 		 (begin
1253              (set! samp (+ samp 1))
1254              (* .5 (oscil osc))))))) ; you can use explicit control panel accessors
1255  */
1256 
add_zeros_to_play_list(int srate,int chans)1257 static bool add_zeros_to_play_list(int srate, int chans)
1258 {
1259   int slot;
1260   slot = find_slot_to_play();
1261   if (slot != -1)
1262     {
1263       dac_info *dp;
1264       dp = make_dac_info(slot, NULL, NULL, NULL, NO_END_SPECIFIED, 0, DAC_NOTHING, Xen_false);
1265       if (dp)
1266 	{
1267 	  start_dac(srate, chans, IN_BACKGROUND, DEFAULT_REVERB_CONTROL_DECAY);
1268 	  return(true);
1269 	}
1270     }
1271   return(false);
1272 }
1273 
1274 
add_xen_to_play_list(Xen func)1275 static bool add_xen_to_play_list(Xen func)
1276 {
1277   int slot;
1278   slot = find_slot_to_play();
1279   if (slot != -1)
1280     {
1281       dac_info *dp;
1282       dp = make_dac_info(slot, NULL, NULL, NULL, NO_END_SPECIFIED, 0, DAC_XEN, func);
1283       if (dp)
1284 	{
1285 #if USE_NO_GUI
1286 	  start_dac((int)mus_srate(), 1, NOT_IN_BACKGROUND, DEFAULT_REVERB_CONTROL_DECAY);
1287 #else
1288 	  start_dac((int)mus_srate(), 1, IN_BACKGROUND, DEFAULT_REVERB_CONTROL_DECAY);
1289 #endif
1290 	  /*                          ^ output channels */
1291 	  return(true);
1292 	}
1293     }
1294   return(false);
1295 }
1296 
1297 
call_start_playing_hook(snd_info * sp)1298 static bool call_start_playing_hook(snd_info *sp)
1299 {
1300   if ((Xen_hook_has_list(start_playing_hook)) &&
1301       (Xen_is_true(run_or_hook(start_playing_hook,
1302 			      Xen_list_1(C_int_to_Xen_sound(sp->index)), /* this can be 123456 (TEMP_SOUND_INDEX in snd-file.c) -- View:Files dialog play button */
1303 			      S_start_playing_hook))))
1304     {
1305       reflect_play_stop(sp);           /* turns off buttons */
1306       if (sp->delete_me)
1307 	completely_free_snd_info(sp);  /* dummy snd_info struct for (play "filename") in snd-xen.c */
1308       return(true);
1309     }
1310   return(false);
1311 }
1312 
1313 
call_start_playing_selection_hook(void)1314 static bool call_start_playing_selection_hook(void)
1315 {
1316   return((Xen_hook_has_list(start_playing_selection_hook)) &&
1317 	 (Xen_is_true(run_or_hook(start_playing_selection_hook,
1318 				 Xen_empty_list,
1319 				 S_start_playing_selection_hook))));
1320 }
1321 
1322 
play_channel_1(chan_info * cp,mus_long_t start,mus_long_t end,play_process_t background,int pos,Xen stop_proc,int out_chan)1323 static dac_info *play_channel_1(chan_info *cp, mus_long_t start, mus_long_t end, play_process_t background,
1324 				int pos, Xen stop_proc, int out_chan)
1325 {
1326   /* just plays one channel (ignores possible sync), includes start-hook */
1327   snd_info *sp;
1328   dac_info *dp = NULL;
1329 
1330   sp = cp->sound;
1331   if (sp->inuse == SOUND_IDLE) return(NULL);
1332   if (call_start_playing_hook(sp)) return(NULL);
1333 
1334   dp = add_channel_to_play_list(cp, sp, start, end, pos, out_chan);
1335   if (dp)
1336     {
1337       dp->stop_procedure = stop_proc;
1338       if (Xen_is_procedure(stop_proc))
1339 	dp->stop_procedure_gc_loc = snd_protect(stop_proc);
1340       set_play_button(sp, true);
1341       start_dac(snd_srate(sp), 1, background, sp->reverb_control_decay);
1342     }
1343   return(dp);
1344 }
1345 
1346 
play_channel(chan_info * cp,mus_long_t start,mus_long_t end)1347 void play_channel(chan_info *cp, mus_long_t start, mus_long_t end)
1348 {
1349   play_channel_1(cp, start, end, IN_BACKGROUND, AT_CURRENT_EDIT_POSITION, Xen_false, cp->chan);
1350 }
1351 
1352 
play_sound_1(snd_info * sp,mus_long_t start,mus_long_t end,play_process_t background,Xen edpos,Xen stop_proc,const char * caller,int arg_pos)1353 static dac_info *play_sound_1(snd_info *sp, mus_long_t start, mus_long_t end, play_process_t background,
1354 			      Xen edpos, Xen stop_proc, const char *caller, int arg_pos)
1355 {
1356   /* just plays one sound (ignores possible sync) */
1357   uint32_t i;
1358   dac_info *dp = NULL, *rtn_dp = NULL;
1359 
1360   if ((background == NOT_IN_BACKGROUND) &&
1361       (play_list_members > 0))
1362     return(NULL);
1363   if (sp->inuse == SOUND_IDLE) return(NULL);
1364   if (call_start_playing_hook(sp)) return(NULL);
1365   for (i = 0; i < sp->nchans; i++)
1366     {
1367       int pos;
1368       pos  = to_c_edit_position(sp->chans[i], edpos, caller, arg_pos);
1369       dp = add_channel_to_play_list(sp->chans[i], sp, start, end, pos, i); /* i = out chan */
1370       if ((dp) && (!rtn_dp)) rtn_dp = dp;
1371     }
1372   if (rtn_dp)
1373     {
1374       rtn_dp->stop_procedure = stop_proc;
1375       if (Xen_is_procedure(stop_proc))
1376 	rtn_dp->stop_procedure_gc_loc = snd_protect(stop_proc);
1377       set_play_button(sp, true);
1378       start_dac(snd_srate(sp), sp->nchans, background, sp->reverb_control_decay);
1379     }
1380   return(rtn_dp);
1381 }
1382 
1383 
play_sound(snd_info * sp,mus_long_t start,mus_long_t end)1384 void play_sound(snd_info *sp, mus_long_t start, mus_long_t end)
1385 {
1386   play_sound_1(sp, start, end, IN_BACKGROUND, C_int_to_Xen_integer(AT_CURRENT_EDIT_POSITION), Xen_false, NULL, 0);
1387 }
1388 
1389 
play_channels_1(chan_info ** cps,int chans,mus_long_t * starts,mus_long_t * ur_ends,play_process_t background,Xen edpos,bool selection,Xen stop_proc,const char * caller,int arg_pos)1390 static dac_info *play_channels_1(chan_info **cps, int chans, mus_long_t *starts, mus_long_t *ur_ends, play_process_t background,
1391 				 Xen edpos, bool selection, Xen stop_proc, const char *caller, int arg_pos)
1392 {
1393   /* ends can be NULL */
1394   int i;
1395   snd_info *sp = NULL;
1396   dac_info *dp = NULL, *rtn_dp = NULL;
1397   mus_long_t *ends;
1398 
1399   if ((background == NOT_IN_BACKGROUND) &&
1400       (play_list_members > 0))
1401     return(NULL);
1402   if (chans <= 0) return(NULL);
1403 
1404   if (!selection)
1405     {
1406       if (call_start_playing_hook(cps[0]->sound)) return(NULL);
1407     }
1408   else
1409     {
1410       if (call_start_playing_selection_hook()) return(NULL);
1411     }
1412 
1413   if (ur_ends)
1414     ends = ur_ends;
1415   else
1416     {
1417       ends = (mus_long_t *)calloc(chans, sizeof(mus_long_t));
1418       for (i = 0; i < chans; i++)
1419 	ends[i] = NO_END_SPECIFIED;
1420     }
1421 
1422   sp = cps[0]->sound;
1423   for (i = 0; i < chans; i++)
1424     {
1425       int pos;
1426       pos  = to_c_edit_position(cps[i], edpos, caller, arg_pos);
1427       dp = add_channel_to_play_list(cps[i], sp, starts[i], ends[i], pos, i);
1428       if (dp)
1429 	{
1430 	  if (!rtn_dp) rtn_dp = dp;
1431 	  if (selection) dp->selection = true;
1432 	}
1433     }
1434   if (!ur_ends) free(ends);
1435   if ((sp) && (rtn_dp))
1436     {
1437       rtn_dp->stop_procedure = stop_proc;
1438       if (Xen_is_procedure(stop_proc))
1439 	rtn_dp->stop_procedure_gc_loc = snd_protect(stop_proc);
1440       set_play_button(sp, true);
1441       start_dac(snd_srate(sp), chans, background, sp->reverb_control_decay);
1442     }
1443   return(rtn_dp);
1444 }
1445 
1446 
play_channels(chan_info ** cps,int chans,mus_long_t * starts,mus_long_t * ur_ends,play_process_t background,Xen edpos,bool selection,const char * caller,int arg_pos)1447 void play_channels(chan_info **cps, int chans, mus_long_t *starts, mus_long_t *ur_ends,
1448 		   play_process_t background, Xen edpos, bool selection, const char *caller, int arg_pos)
1449 {
1450   play_channels_1(cps, chans, starts, ur_ends, background, edpos, selection, Xen_false, caller, arg_pos);
1451 }
1452 
1453 
play_channel_with_sync(chan_info * cp,mus_long_t start,mus_long_t end)1454 void play_channel_with_sync(chan_info *cp, mus_long_t start, mus_long_t end)
1455 {
1456   snd_info *sp;
1457   sync_info *si;
1458   mus_long_t *ends = NULL;
1459   int i;
1460 
1461   sp = cp->sound;
1462   if ((sp->sync == 0) ||
1463       (is_player_sound(sp)))
1464     {
1465       play_channel(cp, start, end);
1466       return;
1467     }
1468 
1469   si = snd_sync(sp->sync);
1470   if (end != NO_END_SPECIFIED)
1471     {
1472       ends = (mus_long_t *)calloc(si->chans, sizeof(mus_long_t));
1473       for (i = 0; i < si->chans; i++) ends[i] = end;
1474     }
1475   for (i = 0; i < si->chans; i++) si->begs[i] = start;
1476 
1477   play_channels_1(si->cps, si->chans, si->begs, ends, IN_BACKGROUND, C_int_to_Xen_integer(AT_CURRENT_EDIT_POSITION), false, Xen_false, NULL, 0);
1478 
1479   free_sync_info(si);
1480   if (ends) free(ends);
1481 }
1482 
1483 
play_selection_1(play_process_t background,Xen stop_proc)1484 static dac_info *play_selection_1(play_process_t background, Xen stop_proc)
1485 {
1486   /* just plays the current selection */
1487   dac_info *dp = NULL;
1488 
1489   if (selection_is_active())
1490     {
1491       sync_info *si;
1492       si = selection_sync();
1493       if (si)
1494 	{
1495 	  int i;
1496 	  mus_long_t *ends;
1497 
1498 	  ends = (mus_long_t *)calloc(si->chans, sizeof(mus_long_t));
1499 	  for (i = 0; i < si->chans; i++)
1500 	    ends[i] = si->begs[i] + selection_len();
1501 
1502 	  dp = play_channels_1(si->cps, si->chans, si->begs, ends, background, C_int_to_Xen_integer(AT_CURRENT_EDIT_POSITION), true, stop_proc, NULL, 0);
1503 	  /* dp->dac_sample is the reader */
1504 	  free_sync_info(si); /* does not free samplers */
1505 	  free(ends);
1506 	}
1507     }
1508   return(dp);
1509 }
1510 
1511 
play_selection(play_process_t background)1512 void play_selection(play_process_t background)
1513 {
1514   play_selection_1(background, Xen_false);
1515 }
1516 
1517 
loop_play_selection(void)1518 void loop_play_selection(void)
1519 {
1520   play_selection_1(IN_BACKGROUND, Xen_true);
1521 }
1522 
1523 
1524 /* -------------------------------- process samples and write to DAC -------------------------------- */
1525 
1526 #define NO_CHANGE 0
1527 #define JUST_AMP 1
1528 #define JUST_SPEED 2
1529 #define ALL_CHANGES 3
1530 
choose_dac_op(dac_info * dp,snd_info * sp)1531 static int choose_dac_op(dac_info *dp, snd_info *sp)
1532 {
1533   if (!sp) return(NO_CHANGE);
1534   if ((dp->expanding) || (dp->filtering) || (dp->reverbing) || (sp->contrast_control_on))
1535     return(ALL_CHANGES);
1536   else
1537     {
1538       if ((sp->speed_control_direction != 1) || (!(snd_feq(sp->speed_control, 1.0))) || (!(snd_feq(dp->cur_srate, 1.0))))
1539 	return(JUST_SPEED);
1540       else
1541 	{
1542 	  if ((AMP_CONTROL(sp, dp) == dp->cur_amp) && (snd_feq(AMP_CONTROL(sp, dp), 1.0)))
1543 	    return(NO_CHANGE);
1544 	  else return(JUST_AMP);
1545 	}
1546     }
1547 }
1548 
1549 static int cursor_time;
1550 /* can't move cursor on each dac buffer -- causes clicks */
1551 
1552 static bool dac_pausing = false;
1553 
toggle_dac_pausing(void)1554 void toggle_dac_pausing(void) {dac_pausing = (!dac_pausing); play_button_pause(dac_pausing);} /* only below and snd-kbd.c */
play_in_progress(void)1555 bool play_in_progress(void) {return(play_list_members > 0);}
1556 
1557 
1558 static unsigned char **audio_bytes = NULL;
1559 static int audio_bytes_size = 0;
1560 static int audio_bytes_devices = 0;
1561 
1562 static mus_float_t **dac_buffers = NULL;
1563 static int dac_buffer_size = 0;
1564 static int dac_buffer_chans = 0; /* chans allocated */
1565 static mus_float_t **rev_ins;
1566 
1567 #define WRITE_TO_DAC 1
1568 #define WRITE_TO_FILE 0
1569 
fill_dac_buffers(int write_ok)1570 static int fill_dac_buffers(int write_ok)
1571 {
1572   /* return value used only by Apply */
1573   int i;
1574   bool cursor_change = false;
1575   int framples;
1576   snd_info *sp;
1577 
1578   framples = snd_dacp->framples;
1579   /* clear buffers */
1580   for (i = 0; i < snd_dacp->channels; i++)
1581     mus_clear_floats(dac_buffers[i], framples);
1582   if (global_rev)
1583     for (i = 0; i < snd_dacp->channels; i++)
1584       mus_clear_floats(rev_ins[i], framples);
1585 
1586   if (dac_pausing)
1587     cursor_change = false;
1588   else
1589     {
1590       dac_info *dp;
1591       mus_float_t *buf;
1592       mus_float_t *revin;
1593 
1594       if (Xen_hook_has_list(play_hook))
1595 	run_hook(play_hook,
1596 		 Xen_list_1(C_int_to_Xen_integer(framples)),
1597 		 S_play_hook);
1598       cursor_time += framples;
1599       cursor_change = (cursor_time >= (int)(snd_dacp->srate * cursor_update_interval(ss)));
1600       /* the check for hooks and so on has to be separate from the fill loop to keep everything synchronized across stop-function replay requests etc */
1601 
1602       /* first check for disasters (user closed sound while playing it or running Apply!) */
1603       for (i = 0; i <= max_active_slot; i++)
1604 	{
1605 	  dp = play_list[i];
1606 	  if (dp)
1607 	    {
1608 	      sp = dp->sp; /* can be nil if region playing */
1609 	      if ((sp) && ((sp->inuse == SOUND_IDLE) || (sp->playing == 0)))
1610 		stop_playing(dp, WITHOUT_HOOK, PLAY_CLOSE);
1611 	    }
1612 	}
1613       /* now read currently active chans and update locations -- don't call any hooks or stop-functions! */
1614       for (i = 0; i <= max_active_slot; i++)
1615 	{
1616 	  dp = play_list[i];
1617 	  if (dp)
1618 	    {
1619 	      int j;
1620 	      mus_float_t amp, incr, sr, sincr, ind, indincr, rev, revincr, fval;
1621 	      if (dp->audio_chan >= snd_dacp->channels) /* this can happen if we're playing 1 chan, try to add 2 chans */
1622 		{
1623 		  if (dac_combines_channels(ss))
1624 		    dp->audio_chan %= snd_dacp->channels;
1625 		  else continue;
1626 		}
1627 
1628 	      /* check for moving cursor */
1629 	      sp = dp->sp;                             /* can be nil if region playing */
1630 	      if ((sp) &&
1631 		  (ss->tracking) &&
1632 		  (cursor_change) &&
1633 		  (dp->chn_fd) &&
1634 		  (!(dp->chn_fd->at_eof)) &&
1635 #if (!USE_NO_GUI)
1636 		  (dp->cp->widgets) &&             /* nil if play_file */
1637 #endif
1638 		  (dp->chn_fd->cb))
1639 		{
1640 		  mus_long_t loc;
1641 		  bool old_just_zero;
1642 		  old_just_zero = dp->cp->just_zero;
1643 		  dp->cp->just_zero = true;
1644 		  loc = current_location(dp->chn_fd);
1645 		  loc -= (int)(cursor_location_offset(ss) * dp->cur_srate); /* should work in either direction */
1646 		  cursor_moveto_without_verbosity(dp->cp, loc);
1647 		  dp->cp->just_zero = old_just_zero;
1648 		}
1649 
1650 	      /* add a buffer's worth from the current source into dp->audio_chan */
1651 	      buf = dac_buffers[dp->audio_chan];
1652 	      if (!buf) continue;
1653 	      revin = rev_ins[dp->audio_chan];
1654 	      switch (choose_dac_op(dp, sp))
1655 		{
1656 		case NO_CHANGE:
1657 		  /* simplest case -- no changes at all */
1658 		  for (j = 0; j < framples; j++)
1659 		    buf[j] += (mus_float_t)((*(dp->dac_sample))(dp));
1660 		  break;
1661 
1662 		case JUST_AMP:
1663 		  /* AMP_CONTROL(sp, dp) is current UI value, dp->cur_amp is current local value */
1664 		  amp = dp->cur_amp;
1665 		  incr = (AMP_CONTROL(sp, dp) - amp) / (mus_float_t)(framples);
1666 		  for (j = 0; j < framples; j++, amp += incr)
1667 		    buf[j] += (mus_float_t)(amp * (*(dp->dac_sample))(dp));
1668 		  dp->cur_amp = amp;
1669 		  break;
1670 
1671 		case JUST_SPEED:
1672 		  /* includes amp changes */
1673 		  /* sp->speed_control is current UI value, dp->cur_srate is current local value */
1674 		  dp->never_sped = false;
1675 		  amp = dp->cur_amp;
1676 		  incr = (AMP_CONTROL(sp, dp) - amp) / (mus_float_t)(framples);
1677 		  sr = dp->cur_srate;
1678 		  sincr = (sp->speed_control * sp->speed_control_direction - sr) / (mus_float_t)(framples);
1679 		  if ((sr != 0.0) || (sincr != 0.0))
1680 		    {
1681 		      if (dp->src)
1682 			{
1683 			  if ((amp == 1.0) && (incr == 0.0) && (sincr == 0.0))
1684 			    {
1685 			      mus_set_increment(dp->src, sr);
1686 			      mus_src_to_buffer(dp->src, &dac_src_input_as_needed, buf, framples);
1687 			    }
1688 			  else
1689 			    {
1690 			      for (j = 0; j < framples; j++, amp += incr, sr += sincr)
1691 				{
1692 				  mus_set_increment(dp->src, sr);
1693 				  buf[j] += (amp * mus_src(dp->src, 0.0, &dac_src_input_as_needed));
1694 				}
1695 			    }
1696 			}
1697 		      else
1698 			{
1699 			  for (j = 0; j < framples; j++, amp += incr, sr += sincr)
1700 			    buf[j] += (amp * speed(dp, sr));
1701 			}
1702 		    }
1703 		  dp->cur_srate = sr;
1704 		  dp->cur_amp = amp;
1705 		  break;
1706 
1707 		case ALL_CHANGES:
1708 		  amp = dp->cur_amp;
1709 		  incr = (AMP_CONTROL(sp, dp) - amp) / (mus_float_t)(framples);
1710 		  sr = dp->cur_srate;
1711 		  sincr = (sp->speed_control * sp->speed_control_direction - sr) / (mus_float_t)(framples);
1712 		  if ((sincr != 0.0) || (!(snd_feq(sr, 1.0)))) dp->never_sped = false;
1713 		  ind = dp->cur_index;
1714 		  indincr = (sp->contrast_control - ind) / (mus_float_t)(framples);
1715 		  rev = dp->cur_rev;
1716 		  revincr = (sp->reverb_control_scale - rev) / (mus_float_t)(framples);
1717 		  if ((dp->filtering) && (sp->filter_control_changed))
1718 		    {
1719 		      mus_float_t *data;
1720 		      data = sample_linear_env(sp->filter_control_envelope, sp->filter_control_order);
1721 		      if (data)
1722 			{
1723 			  /* check all chans in sp before clearing filter_order_changed flag */
1724 			  int j;
1725 			  for (j = i; j <= max_active_slot; j++)
1726 			    {
1727 			      dac_info *ndp;
1728 			      ndp = play_list[j];
1729 			      if ((ndp) &&
1730 				  (ndp->sp == sp) &&
1731 				  (ndp->filtering))
1732 				{
1733 				  if (sp->filter_control_order > ndp->a_size) /* need more room in dp->a == flt->xcoeffs and flt->state */
1734 				    {
1735 				      free(ndp->a);
1736 				      ndp->a_size = sp->filter_control_order;
1737 				      ndp->a = (mus_float_t *)calloc(ndp->a_size, sizeof(mus_float_t));
1738 				    }
1739 				  mus_make_fir_coeffs(sp->filter_control_order, data, ndp->a);      /* fill dp->a with new coeffs */
1740 				  mus_filter_set_xcoeffs(ndp->flt, ndp->a);                         /* tell gen about them */
1741 				  if (mus_filter_set_order(ndp->flt, sp->filter_control_order) < 0) /* fixup gen's order (and internal state array) */
1742 				    snd_warning("trouble in filter (order not changed?)");
1743 				}
1744 			    }
1745 			  free(data);
1746 			}
1747 		      sp->filter_control_changed = false;
1748 		    }
1749 		  if (dp->expanding)
1750 		    {
1751 		      mus_float_t ex, exincr;
1752 		      ex = dp->cur_exp;
1753 		      exincr = (sp->expand_control - ex) / (mus_float_t)(framples);
1754 		      for (j = 0; j < framples; j++, amp += incr, sr += sincr, ind += indincr, ex += exincr, rev += revincr)
1755 			{
1756 			  fval = expand(dp, sr, ex);
1757 			  if (sp->contrast_control_on) fval = contrast(dp, amp, ind, fval); else fval *= amp;
1758 			  if (dp->filtering) fval = mus_fir_filter(dp->flt, fval);
1759 			  if (dp->reverbing) revin[j] += fval * rev;
1760 			  buf[j] += fval;
1761 			}
1762 		      dp->cur_exp = ex;
1763 		    }
1764 		  else
1765 		    {
1766 		      if (dp->filtering)
1767 			{
1768 			  for (j = 0; j < framples; j++, amp += incr, sr += sincr, ind += indincr, rev += revincr)
1769 			    {
1770 			      fval = speed(dp, sr);
1771 			      if (sp->contrast_control_on) fval = contrast(dp, amp, ind, fval); else fval *= amp;
1772 			      fval = mus_fir_filter(dp->flt, fval);
1773 			      if (dp->reverbing) revin[j] += fval * rev;
1774 			      buf[j] += fval;
1775 			    }
1776 			}
1777 		      else
1778 			{
1779 			  if (sp->contrast_control_on)
1780 			    {
1781 			      for (j = 0; j < framples; j++, amp += incr, sr += sincr, ind += indincr, rev += revincr)
1782 				{
1783 				  fval = contrast(dp, amp, ind, speed(dp, sr));
1784 				  if (dp->reverbing) revin[j] += fval * rev;
1785 				  buf[j] += fval;
1786 				}
1787 			    }
1788 			  else
1789 			    {
1790 			      if (dp->never_sped)
1791 				{
1792 				  for (j = 0; j < framples; j++, amp += incr, rev += revincr)
1793 				    {
1794 				      fval = amp * (*(dp->dac_sample))(dp);
1795 				      revin[j] += fval * rev;
1796 				      buf[j] += fval;
1797 				    }
1798 				}
1799 			      else
1800 				{
1801 				  for (j = 0; j < framples; j++, amp += incr, sr += sincr, rev += revincr)
1802 				    {
1803 				      fval = amp * speed(dp, sr);
1804 				      revin[j] += fval * rev;
1805 				      buf[j] += fval;
1806 				    }
1807 				}
1808 			    }
1809 			}
1810 		    }
1811 		  dp->cur_srate = sr;
1812 		  dp->cur_amp = amp;
1813 		  dp->cur_rev = rev;
1814 		  dp->cur_index = ind;
1815 		  break;
1816 		}
1817 	      if ((dp->end != NO_END_SPECIFIED) && (dp->end != 0))
1818 		{
1819 		  if ((!dp->chn_fd->cb) ||
1820 		      ((dp->sp->speed_control_direction > 0) &&
1821 		       ((dp->chn_fd->at_eof) ||
1822 			(dp->end <= current_location(dp->chn_fd)))) ||
1823 		      ((dp->sp->speed_control_direction < 0) &&
1824 			(dp->end > current_location(dp->chn_fd))))
1825 		    dp->end = 0;
1826 		}
1827 	    }
1828 	} /* fill-case loop through max_active_slot */
1829 
1830       if (global_rev)
1831 	for (i = 0; i < framples; i++)
1832 	  reverb(global_rev, rev_ins, dac_buffers, i);
1833 
1834       /* now hooks, stop-function etc */
1835       for (i = 0; i <= max_active_slot; i++)
1836 	{
1837 	  dp = play_list[i];
1838 	  if (dp)
1839 	    {
1840 	      /* check for EOF or specified end point */
1841 	      if (write_ok == WRITE_TO_DAC) /* not Apply */
1842 		{
1843 		  if (dp->end == 0)
1844 		    stop_playing(dp, WITH_HOOK, PLAY_COMPLETE);
1845 		  else
1846 		    {
1847 		      if ((dp->chn_fd) &&
1848 			  (dp->chn_fd->at_eof))
1849 			{
1850 			  if (!(dp->expanding))
1851 			    stop_playing(dp, WITH_HOOK, PLAY_COMPLETE);
1852 			  else
1853 			    {
1854 			      dp->expand_ring_framples -= framples;
1855 			      if (dp->expand_ring_framples <= 0)
1856 				stop_playing(dp, WITH_HOOK, PLAY_COMPLETE);
1857 			    }
1858 			}
1859 		    }
1860 		}
1861 	      else /* Apply, always sets the end point explicitly */
1862 		{
1863 		  if (dp->end == 0)
1864 		    {
1865 		      stop_playing_all_sounds_without_hook(PLAY_COMPLETE);
1866 		      play_list_members = 0;
1867 		      max_active_slot = -1;
1868 		    }
1869 		}
1870 	    }
1871 	} /* stop-case loop through max_active_slot */
1872 
1873       if ((global_rev) &&
1874 	  (snd_dacp) &&
1875 	  (play_list_members == 0))
1876 	{
1877 	  snd_dacp->reverb_ring_framples -= framples;
1878 	  if (snd_dacp->reverb_ring_framples <= 0)
1879 	    free_reverb();
1880 	}
1881     }
1882   /* now parcel these buffers out to the available devices */
1883 
1884   if (snd_dacp)
1885     {
1886 #if (HAVE_OSS || HAVE_ALSA)
1887       if (write_ok == WRITE_TO_DAC)
1888 	{
1889 	  mus_float_t **dev_bufs;
1890 	  dev_bufs = dac_buffers;
1891 	  for (i = 0; i < snd_dacp->devices; i++)
1892 	    if (dev_fd[i] != -1)
1893 	      {
1894 		mus_file_write_buffer(snd_dacp->out_samp_type,
1895 				      0, framples - 1,
1896 				      snd_dacp->chans_per_device[i],
1897 				      dev_bufs,
1898 				      (char *)(audio_bytes[i]),
1899 				      clipping(ss));
1900 		dev_bufs += snd_dacp->chans_per_device[i];
1901 	      }
1902 	  for (i = 0; i < snd_dacp->devices; i++)
1903 	    if (dev_fd[i] != -1)
1904 	      {
1905 		int bytes;
1906 		bytes = snd_dacp->chans_per_device[i] * framples * mus_bytes_per_sample(snd_dacp->out_samp_type);
1907 		mus_audio_write(dev_fd[i], (char *)(audio_bytes[i]), bytes);
1908 	      }
1909 	}
1910 #else
1911       if (write_ok == WRITE_TO_DAC)
1912 	{
1913 	  int bytes;
1914 	  mus_file_write_buffer(snd_dacp->out_samp_type, 0, framples - 1, snd_dacp->channels, dac_buffers, (char *)(audio_bytes[0]), clipping(ss));
1915 	  bytes = snd_dacp->channels * framples * mus_bytes_per_sample(snd_dacp->out_samp_type);
1916 	  mus_audio_write(dev_fd[0], (char *)(audio_bytes[0]), bytes);
1917 	}
1918 #endif
1919     }
1920   if (cursor_change) cursor_time = 0;
1921   return(framples);
1922 }
1923 
1924 
1925 /* -------------------------------- specialize mus_print -------------------------------- */
1926 
1927 static mus_print_handler_t *old_dac_printer = NULL;
1928 static char *last_print = NULL;
1929 
dac_mus_print(char * msg)1930 static void dac_mus_print(char *msg)
1931 {
1932   if (last_print) free(last_print);
1933   last_print = mus_strdup(msg);
1934   (*old_dac_printer)(msg);
1935 }
1936 
1937 
set_dac_print(void)1938 static void set_dac_print(void)
1939 {
1940   if (last_print) free(last_print);
1941   last_print = NULL;
1942   if (old_dac_printer != dac_mus_print)
1943     old_dac_printer = mus_print_set_handler(dac_mus_print);
1944 }
1945 
1946 
unset_dac_print(void)1947 static void unset_dac_print(void)
1948 {
1949   mus_print_set_handler(old_dac_printer);
1950 }
1951 
1952 
1953 #if WITH_AUDIO
describe_dac(void)1954 static const char *describe_dac(void)
1955 {
1956   /* this is only called in dac_error and only then upon a failed mus_audio_open_output */
1957   int players = 0, i;
1958   dac_info *ptr = NULL;
1959   for (i = 0; i < dac_max_sounds; i++)
1960     if (play_list[i])
1961       {
1962 	ptr = play_list[i];
1963 	players++;
1964       }
1965   if ((players == 1) &&
1966       (ptr->sp))
1967     return((const char *)(ptr->sp->short_filename));
1968   return("");
1969 }
1970 #endif
1971 
1972 
dac_error(void)1973 static void dac_error(void)
1974 {
1975   stop_playing_all_sounds_without_hook(PLAY_ERROR);
1976 #if WITH_AUDIO
1977   snd_error("can't play %s: %s",
1978 	    describe_dac(),
1979 	    (last_print) ? last_print : "reason not known");
1980 #endif
1981 }
1982 
1983 
1984 /* -------------------------------- initialize DAC -------------------------------- */
1985 
make_dac_buffers(void)1986 static void make_dac_buffers(void)
1987 {
1988   /* make the per-channel buffers and audio output buffers */
1989   int bytes, i;
1990 
1991   if ((!dac_buffers) ||
1992       (dac_buffer_chans < snd_dacp->channels) ||
1993       (dac_buffer_size < snd_dacp->framples))
1994     {
1995       if (dac_buffers)
1996 	{
1997 	  for (i = 0; i < dac_buffer_chans; i++) free(dac_buffers[i]);
1998 	  free(dac_buffers);
1999 	}
2000       if (rev_ins)
2001 	{
2002 	  for (i = 0; i < dac_buffer_chans; i++) free(rev_ins[i]);
2003 	  free(rev_ins);
2004 	}
2005       dac_buffers = (mus_float_t **)calloc(snd_dacp->channels, sizeof(mus_float_t *));
2006       rev_ins = (mus_float_t **)calloc(snd_dacp->channels, sizeof(mus_float_t *));
2007       for (i = 0; i < snd_dacp->channels; i++)
2008 	{
2009 	  dac_buffers[i] = (mus_float_t *)calloc(snd_dacp->framples, sizeof(mus_float_t));
2010 	  rev_ins[i] = (mus_float_t *)calloc(snd_dacp->framples, sizeof(mus_float_t));
2011 	}
2012       dac_buffer_chans = snd_dacp->channels;
2013       dac_buffer_size = snd_dacp->framples;
2014       if (r_outs) free(r_outs);
2015       if (r_ins) free(r_ins);
2016       r_outs = (mus_float_t *)calloc(snd_dacp->channels, sizeof(mus_float_t));
2017       r_ins = (mus_float_t *)calloc(snd_dacp->channels, sizeof(mus_float_t));
2018     }
2019 
2020   bytes = snd_dacp->channels * dac_buffer_size * mus_bytes_per_sample(snd_dacp->out_samp_type);
2021   if ((audio_bytes_size < bytes) ||
2022       (audio_bytes_devices < snd_dacp->devices))
2023     {
2024       if (audio_bytes)
2025 	{
2026 	  for (i = 0; i < audio_bytes_devices; i++) free(audio_bytes[i]);
2027 	  free(audio_bytes);
2028 	}
2029       audio_bytes = (unsigned char **)calloc(snd_dacp->devices, sizeof(unsigned char *));
2030       for (i = 0; i < snd_dacp->devices; i++)
2031 	audio_bytes[i] = (unsigned char *)calloc(bytes, sizeof(unsigned char));
2032       audio_bytes_size = bytes;
2033       audio_bytes_devices = snd_dacp->devices;
2034     }
2035 }
2036 
2037 
2038 static void stop_audio_output(void);
2039 
2040 #if (HAVE_ALSA || HAVE_OSS)
2041 
2042 
2043 /* Controls behavior of device selection logic below. No amount of logic
2044  * can make everybody happy all the time. The [i]logic below cannot always
2045  * produce the desired result but deleting it altogether will break the
2046  * systems that currently rely on it. Not wise without an external api
2047  * in place designed to select whatever the user _really_ wants. Till
2048  * then set this to "1" to always send to the first device. */
2049 
2050 static int feed_first_device = 0;
2051 
2052 #define ALSA_MAX_DEVICES 64
2053 static int alsa_devices[ALSA_MAX_DEVICES];
2054 static int alsa_available_chans[ALSA_MAX_DEVICES];
2055 static int alsa_max_chans[ALSA_MAX_DEVICES];
2056 static int alsa_min_chans[ALSA_MAX_DEVICES];
2057 
2058 static int alsa_max_chans_value = 0;
2059 static int alsa_max_chans_dev = 0;
2060 static bool audio_devices_scanned = false;
2061 static int alsa_devices_available = 0;
2062 
2063 void mus_audio_alsa_channel_info(int dev, int *info);
2064 void mus_audio_alsa_device_list(int sys, int size, int *val);
2065 int mus_audio_alsa_device_direction(int dev);
2066 
scan_audio_devices(void)2067 static void scan_audio_devices(void)
2068 {
2069   int cards, card, devs, dev, d;
2070   int index = 0;
2071   int direction;
2072   int val[ALSA_MAX_DEVICES];
2073   if (!audio_devices_scanned)
2074     {
2075       audio_devices_scanned = true;
2076        /* At this time
2077        * we always select the widest device if the requested channels fit into it.
2078        * Otherwise we try to combine devices, if all fails we modify snd settings
2079        * so that channel folding takes place. This is inefficient but works for now.
2080        */
2081       cards = 1;
2082       index = 0;
2083       /* scan all cards and build a list of available output devices */
2084       for (card = 0; card < cards; card++)
2085 	{
2086 	  mus_audio_alsa_device_list(MUS_AUDIO_PACK_SYSTEM(card), ALSA_MAX_DEVICES, val);
2087 	  devs = val[0];
2088 	  /* scan all devices in the card */
2089 	  for (d = 0; d < devs; d++)
2090 	    {
2091 	      dev = val[d + 1];
2092 	      direction = mus_audio_alsa_device_direction(MUS_AUDIO_PACK_SYSTEM(card) | dev);
2093 	      if (direction == 0)
2094 		{
2095 		  /* remember output device */
2096 		  alsa_devices[index++] = MUS_AUDIO_PACK_SYSTEM(card) | dev;
2097 		  if (index >= ALSA_MAX_DEVICES) goto NO_MORE_DEVICES;
2098 		}
2099 	    }
2100 	}
2101     NO_MORE_DEVICES:
2102       /* get channel availability for all devices */
2103       for (d = 0; d < index; d++)
2104 	{
2105 	  int chan_info[4];
2106 	  alsa_available_chans[d] = 0;
2107 	  alsa_min_chans[d] = 0;
2108 	  alsa_max_chans[d] = 0;
2109 	  mus_audio_alsa_channel_info(alsa_devices[d], chan_info);
2110 	  alsa_available_chans[d] = chan_info[0];
2111 	  alsa_min_chans[d] = chan_info[1];
2112 	  alsa_max_chans[d] = chan_info[2];
2113 	  if (alsa_max_chans[d] > alsa_max_chans_value)
2114 	    {
2115 	      /* remember widest device */
2116 	      alsa_max_chans_value = alsa_max_chans[d];
2117 	      alsa_max_chans_dev = d;
2118 	    }
2119 	}
2120     }
2121   alsa_devices_available = index;
2122 }
2123 
2124 int mus_audio_alsa_samples_per_channel(int dev);
2125 
start_audio_output_1(void)2126 static bool start_audio_output_1(void)
2127 {
2128   int i;
2129 
2130   /* -------------------- ALSA not OSS -------------------- */
2131   if (mus_audio_api() == MUS_ALSA_API)
2132     {
2133       static int out_dev[ALSA_MAX_DEVICES];
2134       int d, alloc_chans, alloc_devs;
2135       scan_audio_devices();
2136       /* allocate devices for playback */
2137       alloc_chans = 0;
2138       alloc_devs = 0;
2139       for (d = 0; d < ALSA_MAX_DEVICES; d++) out_dev[d] = -1;
2140       for (d = 0; d < MAX_DEVICES; d++) dev_fd[d] = -1;
2141       if (feed_first_device == 0)
2142 	{
2143 	  /* see if widest device can accommodate all channels */
2144 	  if (alsa_max_chans_value >= snd_dacp->channels)
2145 	    {
2146 	      out_dev[alloc_devs++] = alsa_max_chans_dev;
2147 	      alloc_chans += alsa_max_chans_value;
2148 	    }
2149 	  if (alloc_devs == 0)
2150 	    {
2151 	      /* try to use several devices */
2152 	      int prev_samp_type = -1;
2153 	      for (d = 0; d < alsa_devices_available; d++)
2154 		{
2155 		  int this_samp_type;
2156 		  this_samp_type = mus_audio_compatible_sample_type(alsa_devices[d]);
2157 		  if (prev_samp_type == -1)
2158 		    {
2159 		      prev_samp_type = this_samp_type;
2160 		    }
2161 		  /* samp_type for all selected devices should match */
2162 		  if (this_samp_type == prev_samp_type)
2163 		    {
2164 		      out_dev[alloc_devs++] = d;
2165 		      alloc_chans += alsa_available_chans[d];
2166 		      if (alloc_devs >= ALSA_MAX_DEVICES)
2167 			break;
2168 		    }
2169 		}
2170 	      if ((alloc_devs != 0) && (alloc_chans < snd_dacp->channels))
2171 		{
2172 		  /* not enough available channels, give up */
2173 		  for (d = 0; d < ALSA_MAX_DEVICES; d++) out_dev[d] = -1;
2174 		  alloc_devs = 0;
2175 		  alloc_chans = 0;
2176 		}
2177 	      if (alloc_devs == 0)
2178 		{
2179 		  /* fold all channels into the first device */
2180 		  out_dev[alloc_devs++] = 0;
2181 		  alloc_chans += alsa_available_chans[0];
2182 		}
2183 	    }
2184 	}
2185       else
2186 	{
2187 	  /* first device on first card is the one */
2188 	  out_dev[alloc_devs++] = 0;
2189 	  alloc_chans += alsa_available_chans[0];
2190 	}
2191       snd_dacp->out_samp_type = mus_audio_compatible_sample_type(alsa_devices[out_dev[0]]);
2192       if (alloc_devs < 2)
2193 	{
2194 	  /* see if we have a minimum sized frample to fill
2195 	   * FIXME: could this happen in more than one device? */
2196 	  int c;
2197 	  c = alsa_min_chans[out_dev[0]];
2198 	  if (c > snd_dacp->channels)
2199 	    {
2200 	      snd_dacp->channels = c;
2201 	    }
2202 	}
2203       /* see if we have to fold channels */
2204       if (alloc_chans < snd_dacp->channels)
2205 	{
2206 	  if (dac_combines_channels(ss))
2207 	    snd_warning("folding %d chans into %d ",
2208 			snd_dacp->channels, alloc_chans);
2209 	  snd_dacp->channels = alloc_chans;
2210 	}
2211 
2212       {
2213 	int samples_per_channel;
2214         /* read the number of samples per channel the device wants buffered */
2215 	samples_per_channel = mus_audio_alsa_samples_per_channel(alsa_devices[out_dev[0]]);
2216 	snd_dacp->framples = samples_per_channel;
2217 	/* set_dac_size(snd_dacp->framples * mus_bytes_per_sample(snd_dacp->out_samp_type)); */
2218 	set_dac_size(samples_per_channel); /* bil 24-Mar-13 */
2219       }
2220       /* open all allocated devices */
2221       for (d = 0; d < alloc_devs; d++)
2222 	{
2223 	  int channels;
2224 	  channels = alsa_available_chans[out_dev[d]];
2225 	  if (alloc_chans <= alsa_available_chans[out_dev[d]])
2226 	    {
2227 	      if (snd_dacp->channels < alsa_min_chans[out_dev[d]])
2228 		{
2229 		  channels = alsa_min_chans[out_dev[d]];
2230 		}
2231 	      else
2232 		{
2233 		  channels = snd_dacp->channels;
2234 		}
2235 	    }
2236 	  /* FIXME: assumes devices are same size... */
2237 	  set_dac_print();
2238 	  dev_fd[d] = mus_audio_open_output(alsa_devices[out_dev[d]],
2239 					    snd_dacp->srate,
2240 					    channels,
2241 					    snd_dacp->out_samp_type,
2242 					    snd_dacp->framples * channels * mus_bytes_per_sample(snd_dacp->out_samp_type));
2243 	  unset_dac_print();
2244 
2245 	  if (dev_fd[d] == -1)
2246 	    {
2247 	      /* could not open a device, close all others and quit playback */
2248 	      int i;
2249 	      for (i = 0; i < d; i++)
2250 		{
2251 		  mus_audio_close(alsa_devices[out_dev[i]]);
2252 		}
2253 	      dac_error();
2254 	      dac_running = false;
2255 	      if (global_rev) free_reverb();
2256 	      max_active_slot = -1;
2257 	      return(false);
2258 	    }
2259 	}
2260       snd_dacp->devices = (alloc_devs > 1000000) ? 1000000 : alloc_devs; /* placate damned gcc */
2261       /* for now assume all are same number of chans */
2262       snd_dacp->chans_per_device = (int *)calloc(snd_dacp->devices, sizeof(int));
2263       for (i = 0; i < snd_dacp->devices; i++)
2264 	snd_dacp->chans_per_device[i] = snd_dacp->channels / snd_dacp->devices;
2265       make_dac_buffers();
2266     }
2267   else
2268     {
2269 
2270       /* -------------------- OSS not ALSA -------------------- */
2271       /* api == MUS_OSS_API -- MUS_JACK_API should not intrude here because we're in HAVE_ALSA || HAVE_OSS */
2272       int oss_available_chans = 2;
2273 
2274       if (snd_dacp->channels > 2)
2275 	oss_available_chans = mus_audio_device_channels(MUS_AUDIO_DEFAULT);
2276       for (i = 0; i < MAX_DEVICES; i++) dev_fd[i] = -1;
2277       /* see if we can play 16 bit output */
2278       snd_dacp->out_samp_type = mus_audio_compatible_sample_type(MUS_AUDIO_DEFAULT);
2279 
2280       /* removed PPC stuff here 30-Apr-12 */
2281 
2282       if ((oss_available_chans < snd_dacp->channels) &&
2283           (oss_available_chans > 0))
2284  	{
2285 	  if (dac_combines_channels(ss))
2286 	    snd_warning("folding %d chans into %d ",
2287 			snd_dacp->channels, oss_available_chans);
2288 	  snd_dacp->channels = oss_available_chans;
2289 	}
2290       set_dac_print();
2291       if (dev_fd[0] == MUS_ERROR)
2292 	dev_fd[0] = mus_audio_open_output(MUS_AUDIO_DEFAULT,
2293 					  snd_dacp->srate, snd_dacp->channels,
2294 					  snd_dacp->out_samp_type,
2295 					  dac_size(ss));
2296       unset_dac_print();
2297       if (dev_fd[0] == MUS_ERROR)
2298 	{
2299 	  dac_error();
2300 	  stop_audio_output();
2301 	  return(false);
2302 	}
2303       snd_dacp->devices = (dev_fd[1] != -1) ? 2 : 1;
2304       snd_dacp->chans_per_device = (int *)calloc(snd_dacp->devices, sizeof(int));
2305       for (i = 0; i < snd_dacp->devices; i++)
2306 	snd_dacp->chans_per_device[i] = snd_dacp->channels / snd_dacp->devices;
2307       make_dac_buffers();
2308     }
2309   return(true);
2310 }
2311 #else /* not ALSA or OSS */
2312 
start_audio_output_1(void)2313 static bool start_audio_output_1(void)
2314 {
2315   int i;
2316   int available_chans = 2;
2317 
2318   if (snd_dacp->channels > 2)
2319     available_chans = mus_audio_device_channels(MUS_AUDIO_DEFAULT);
2320 
2321   if (available_chans <= 0)
2322     {
2323       snd_warning("no available channels??");
2324       return(false);
2325     }
2326   if (available_chans < snd_dacp->channels)
2327     {
2328       if (dac_combines_channels(ss))
2329 	snd_warning("folding %d chans into %d ",
2330 		    snd_dacp->channels,
2331 		    available_chans);
2332       snd_dacp->channels = available_chans;
2333     }
2334 
2335   for (i = 0; i < MAX_DEVICES; i++) dev_fd[i] = -1;
2336   snd_dacp->out_samp_type = mus_audio_device_sample_type(MUS_AUDIO_DEFAULT);
2337 
2338   set_dac_print();
2339   if (dev_fd[0] == MUS_ERROR)
2340     dev_fd[0] = mus_audio_open_output(MUS_AUDIO_DEFAULT,
2341 				      snd_dacp->srate,
2342 				      snd_dacp->channels,
2343 				      snd_dacp->out_samp_type,
2344 				      dac_size(ss));
2345   unset_dac_print();
2346   if (dev_fd[0] == MUS_ERROR)
2347     {
2348       dac_error();
2349       stop_audio_output();
2350       return(false);
2351     }
2352   snd_dacp->devices = 1;
2353   snd_dacp->chans_per_device = (int *)calloc(snd_dacp->devices, sizeof(int));
2354   for (i = 0; i < snd_dacp->devices; i++)
2355     snd_dacp->chans_per_device[i] = available_chans / snd_dacp->devices;
2356   make_dac_buffers();
2357   return(true);
2358 }
2359 #endif
2360 
2361 
start_audio_output(void)2362 static bool start_audio_output(void)
2363 {
2364   /* at this point the desired output srate and chans are set in dacp (via start_dac) */
2365   cursor_time = 0;
2366   if (start_audio_output_1()) /* number of channels may be less than requested initially */
2367     {
2368       int i;
2369       for (i = 0; i <= max_active_slot; i++)
2370 	{
2371 	  dac_info *dp;
2372 	  dp = play_list[i];
2373 	  if (dp)
2374 	    {
2375 	      /* deferred reverb allocation since start_audio_output_1 may force more chans open */
2376 	      if ((dp->reverbing) &&
2377 		  (dp->sp) &&
2378 		  (!global_rev))
2379 		make_reverb(dp->sp, snd_dacp->channels);
2380 
2381 	      if (dp->audio_chan >= snd_dacp->channels)
2382 		{
2383 		  if (dac_combines_channels(ss))
2384 		    dp->audio_chan %= snd_dacp->channels;
2385 		  else stop_playing(dp, WITHOUT_HOOK, PLAY_NO_CHANNEL);
2386 		}
2387 	    }
2388 	}
2389       dac_running = true;
2390       fill_dac_buffers(WRITE_TO_DAC);
2391       if (!snd_dacp) return(false);
2392       return(true);
2393     }
2394   return(false);
2395 }
2396 
2397 
stop_audio_output(void)2398 static void stop_audio_output(void)
2399 {
2400    int i;
2401    for (i = 0; i < MAX_DEVICES; i++)
2402      if (dev_fd[i] != -1)
2403        {
2404 	 mus_audio_close(dev_fd[i]);
2405 	 dev_fd[i] = -1;
2406        }
2407    dac_running = false;
2408    dac_pausing = false;
2409    if (global_rev) free_reverb();
2410    max_active_slot = -1;
2411 }
2412 
2413 
dac_in_background(any_pointer_t ptr)2414 static idle_func_t dac_in_background(any_pointer_t ptr)
2415 {
2416   /* slice 0: try to open audio output devices and get ready to send samples
2417    *       1: loop sending data until the play_list is empty or some error (C-g) happens
2418    *       2: try to close all active outputs and remove background procedure
2419    */
2420   if (!snd_dacp) return(BACKGROUND_QUIT);
2421   switch (snd_dacp->slice)
2422     {
2423     case 0:
2424       if (start_audio_output())
2425 	{
2426 	  if (!snd_dacp) return(BACKGROUND_QUIT);
2427 	  snd_dacp->slice = 1;
2428 	  return(BACKGROUND_CONTINUE);
2429 	}
2430       else
2431 	{
2432 	  free_dac_state();
2433 	  return(BACKGROUND_QUIT);
2434 	}
2435       break;
2436 
2437     case 1:
2438       fill_dac_buffers(WRITE_TO_DAC);
2439       if (!snd_dacp) return(BACKGROUND_QUIT); /* dac-hook called stop-playing or something equally perverse */
2440       if ((!global_rev) && (play_list_members == 0)) snd_dacp->slice = 2;
2441       return(BACKGROUND_CONTINUE);
2442       break;
2443 
2444      case 2:
2445        stop_audio_output();
2446        free_dac_state();
2447        return(BACKGROUND_QUIT);
2448        break;
2449      }
2450   return(BACKGROUND_QUIT);
2451 }
2452 
2453 
2454 
2455 /* ---------------- support for Apply button (snd-apply.c) ---------------- */
2456 
initialize_apply(snd_info * sp,int chans,mus_long_t beg,mus_long_t dur)2457 void initialize_apply(snd_info *sp, int chans, mus_long_t beg, mus_long_t dur)
2458 {
2459   int curchan = 0;
2460 
2461   stop_playing_all_sounds_without_hook(PLAY_APPLY);
2462 
2463   if (snd_dacp)                             /* may not have been able to complete despite slice=2 in stop all */
2464     {
2465       snd_dacp->slice = 2;
2466       dac_in_background(NULL);              /* so try to force completion */
2467     }
2468 
2469   if (chans <= 0) return;
2470   max_active_slot = -1;
2471   play_list_members = 0;
2472   dac_running = true; /* this keeps start_dac from actually starting the dac */
2473   if (snd_dacp) free_dac_state();
2474 
2475   snd_dacp = (dac_state *)calloc(1, sizeof(dac_state));
2476   snd_dacp->slice = 0;
2477   snd_dacp->srate = snd_srate(sp);
2478   snd_dacp->out_samp_type = MUS_AUDIO_COMPATIBLE_SAMPLE_TYPE;
2479   if (snd_dacp->srate <= 0) snd_dacp->srate = 44100;
2480   snd_dacp->channels = chans;
2481   snd_dacp->framples = 8192;
2482   snd_dacp->devices = 1;
2483   snd_dacp->chans_per_device = (int *)calloc(1, sizeof(int));
2484   snd_dacp->chans_per_device[0] = chans;
2485   snd_dacp->reverb_ring_framples = (mus_long_t)(snd_dacp->srate * sp->reverb_control_decay);
2486   make_dac_buffers();
2487 
2488   switch (ss->apply_choice)
2489     {
2490     case APPLY_TO_SOUND:
2491       play_sound(sp, beg, beg + dur);
2492       break;
2493 
2494     case APPLY_TO_SELECTION:
2495       play_selection(IN_BACKGROUND);
2496       break;
2497 
2498     case APPLY_TO_CHANNEL:
2499       if (sp->selected_channel != NO_SELECTION)
2500 	curchan = sp->selected_channel;
2501       play_channel(sp->chans[curchan], beg, beg + dur);
2502       break;
2503     }
2504 }
2505 
2506 
finalize_apply(snd_info * sp)2507 void finalize_apply(snd_info *sp)
2508 {
2509   /* if no reverb, these need to be cleaned up */
2510   stop_playing_all_sounds_without_hook(PLAY_APPLY);
2511   max_active_slot = -1;
2512   play_list_members = 0;
2513   sp->playing = 0;
2514   dac_running = false;
2515   if (snd_dacp) free_dac_state();
2516   if (global_rev) free_reverb();
2517 }
2518 
2519 
run_apply(int ofd)2520 int run_apply(int ofd)
2521 {
2522   int len, chans;
2523   if (!(snd_dacp)) return(-1);
2524   chans = snd_dacp->channels; /* snd_dacp might be freed by fill_dac_buffers */
2525   len = fill_dac_buffers(WRITE_TO_FILE);
2526   mus_file_write(ofd, 0, len - 1, chans, dac_buffers);
2527   return(len);
2528 }
2529 
2530 
2531 
2532 /* -------- players -------- */
2533 
2534 static snd_info **players = NULL;
2535 static int *player_chans = NULL;
2536 static int players_size = 0;
2537 
new_player_index(void)2538 static int new_player_index(void)
2539 {
2540   int i, old_size;
2541 
2542   if (players_size == 0)
2543     {
2544       players_size = 8;
2545       players = (snd_info **)calloc(players_size, sizeof(snd_info *));
2546       player_chans = (int *)calloc(players_size, sizeof(int));
2547       return(-1);
2548     }
2549 
2550   for (i = 1; i < players_size; i++)
2551     if (!players[i])
2552       return(-i);
2553 
2554   old_size = players_size;
2555   players_size += 8;
2556   players = (snd_info **)realloc(players, players_size * sizeof(snd_info *));
2557   player_chans = (int *)realloc(player_chans, players_size * sizeof(int));
2558   for (i = old_size; i < players_size; i++)
2559     {
2560       players[i] = NULL;
2561       player_chans[i] = 0;
2562     }
2563   return(-old_size);
2564 }
2565 
2566 #define PLAYER(Sp) -(Sp->index)
2567 
make_player(snd_info * sp,chan_info * cp)2568 static int make_player(snd_info *sp, chan_info *cp)
2569 {
2570   /* store sp so we can access it via find_sound (get_sp) later */
2571   players[PLAYER(sp)] = sp;
2572   player_chans[PLAYER(sp)] = cp->chan;
2573   return(-sp->index);
2574 }
2575 
free_player_sound(snd_info * sp)2576 static void free_player_sound(snd_info *sp)
2577 {
2578   if (players)
2579     {
2580       players[PLAYER(sp)] = NULL;
2581       player_chans[PLAYER(sp)] = 0;
2582     }
2583   free(sp->filename);
2584   sp->filename = NULL;
2585   free(sp->chans);
2586   sp->chans = NULL;
2587   if (sp->filter_control_envelope) sp->filter_control_envelope = free_env(sp->filter_control_envelope);
2588   sp->inuse = SOUND_IDLE;
2589   free(sp);
2590 }
2591 
2592 
clear_players(void)2593 void clear_players(void)
2594 {
2595   /* called only in free_snd_info, snd-data.c -- make sure currently closing sound is not playing (via a user-created player) */
2596   int i;
2597   for (i = 0; i < players_size; i++)
2598     {
2599       uint32_t j;
2600       snd_info *sp;
2601       sp = players[i];
2602       if (sp)
2603 	for (j = 0; j < sp->nchans; j++)
2604 	  if ((!sp->chans[j]) ||
2605 	      (sp->chans[j]->active < CHANNEL_HAS_EDIT_LIST) ||
2606 	      (!sp->chans[j]->sound))
2607 	    {
2608 	      int k;
2609 	      for (k = 0; k <= max_active_slot; k++)
2610 		{
2611 		  dac_info *dp;
2612 		  dp = play_list[k];
2613 		  if ((dp) && (sp == dp->sp))
2614 		    {
2615 		      play_list[k] = NULL;
2616 		      play_list_members--;
2617 		      free_dac_info(dp, PLAY_CLOSE); /* calls stop-function, if any. used in snd-data.c in free_snd_info */
2618 		    }
2619 		}
2620 	      if (is_player_sound(sp)) free_player_sound(sp);
2621 	      break;
2622 	    }
2623     }
2624 }
2625 
2626 
2627 /* ---------------------------------------- player objects ---------------------------------------- */
2628 
2629 typedef struct {
2630   int n;
2631 } xen_player;
2632 
2633 
2634 #define Xen_to_xen_player(arg) ((xen_player *)Xen_object_ref(arg))
2635 
xen_player_to_int(Xen n)2636 static int xen_player_to_int(Xen n)
2637 {
2638   xen_player *mx;
2639   mx = Xen_to_xen_player(n);
2640   return(mx->n);
2641 }
2642 
2643 #define Xen_player_to_C_int(Player) xen_player_to_int(Player)
2644 
2645 
get_player_sound(Xen obj)2646 snd_info *get_player_sound(Xen obj)
2647 {
2648   return(players[xen_player_to_int(obj)]);
2649 }
2650 
2651 
2652 static Xen_object_type_t xen_player_tag;
2653 
xen_is_player(Xen obj)2654 bool xen_is_player(Xen obj)
2655 {
2656   return(Xen_c_object_is_type(obj, xen_player_tag));
2657 }
2658 
2659 
2660 #if (!HAVE_SCHEME)
xen_player_free(xen_player * v)2661 static void xen_player_free(xen_player *v) {if (v) free(v);}
2662 
Xen_wrap_free(xen_player,free_xen_player,xen_player_free)2663 Xen_wrap_free(xen_player, free_xen_player, xen_player_free)
2664 #else
2665 static s7_pointer s7_xen_player_free(s7_scheme *sc, s7_pointer obj)
2666 {
2667   xen_player *v;
2668   v = (xen_player *)s7_c_object_value(obj);
2669   if (v) free(v);  /* can it be NULL? */
2670   return(NULL);
2671 }
2672 #endif
2673 
2674 static char *xen_player_to_string(xen_player *v)
2675 {
2676   #define PLAYER_PRINT_BUFFER_SIZE 64
2677   char *buf;
2678   if (!v) return(NULL);
2679   buf = (char *)calloc(PLAYER_PRINT_BUFFER_SIZE, sizeof(char));
2680   snprintf(buf, PLAYER_PRINT_BUFFER_SIZE, "#<player %d>", v->n);
2681   return(buf);
2682 }
2683 
2684 
2685 #if HAVE_FORTH || HAVE_RUBY
Xen_wrap_print(xen_player,print_xen_player,xen_player_to_string)2686 Xen_wrap_print(xen_player, print_xen_player, xen_player_to_string)
2687 
2688 static Xen g_xen_player_to_string(Xen obj)
2689 {
2690   char *vstr;
2691   Xen result;
2692   #define S_xen_player_to_string "player->string"
2693   Xen_check_type(xen_is_player(obj), obj, 1, S_xen_player_to_string, "a player");
2694   vstr = xen_player_to_string(Xen_to_xen_player(obj));
2695   result = C_string_to_Xen_string(vstr);
2696   free(vstr);
2697   return(result);
2698 }
2699 #else
2700 #if HAVE_SCHEME
g_xen_player_to_string(s7_scheme * sc,s7_pointer args)2701 static s7_pointer g_xen_player_to_string(s7_scheme *sc, s7_pointer args)
2702 {
2703   char *vstr;
2704   Xen result;
2705   vstr = xen_player_to_string(Xen_to_xen_player(s7_car(args)));
2706   result = C_string_to_Xen_string(vstr);
2707   free(vstr);
2708   return(result);
2709 }
2710 #endif
2711 #endif
2712 
2713 
2714 #if (!HAVE_SCHEME)
xen_player_equalp(xen_player * v1,xen_player * v2)2715 static bool xen_player_equalp(xen_player *v1, xen_player *v2)
2716 {
2717   return((v1 == v2) ||
2718 	 (v1->n == v2->n));
2719 }
2720 
equalp_xen_player(Xen obj1,Xen obj2)2721 static Xen equalp_xen_player(Xen obj1, Xen obj2)
2722 {
2723   if ((!(xen_is_player(obj1))) || (!(xen_is_player(obj2)))) return(Xen_false);
2724   return(C_bool_to_Xen_boolean(xen_player_equalp(Xen_to_xen_player(obj1), Xen_to_xen_player(obj2))));
2725 }
2726 #endif
2727 
2728 
xen_player_make(int n)2729 static xen_player *xen_player_make(int n)
2730 {
2731   xen_player *new_v;
2732   new_v = (xen_player *)malloc(sizeof(xen_player));
2733   new_v->n = n;
2734   return(new_v);
2735 }
2736 
2737 
new_xen_player(int n)2738 static Xen new_xen_player(int n)
2739 {
2740   xen_player *mx;
2741   if (n < 0)
2742     return(Xen_false);
2743 
2744   mx = xen_player_make(n);
2745   return(Xen_make_object(xen_player_tag, mx, 0, free_xen_player));
2746 }
2747 
2748 #define C_int_to_Xen_player(val) new_xen_player(val)
2749 
2750 
2751 #if HAVE_SCHEME
s7_xen_player_is_equal(s7_scheme * sc,s7_pointer args)2752 static s7_pointer s7_xen_player_is_equal(s7_scheme *sc, s7_pointer args)
2753 {
2754   s7_pointer p1, p2;
2755   p1 = s7_car(args);
2756   p2 = s7_cadr(args);
2757   if (p1 == p2) return(s7_t(sc));
2758   if (s7_c_object_type(p2) == xen_player_tag)
2759     return(s7_make_boolean(sc, (((xen_player *)s7_c_object_value(p1))->n == ((xen_player *)s7_c_object_value(p2))->n)));
2760   return(s7_f(sc));
2761 }
2762 
s7_xen_player_length(s7_scheme * sc,s7_pointer args)2763 static s7_pointer s7_xen_player_length(s7_scheme *sc, s7_pointer args)
2764 {
2765   chan_info *cp;
2766   int index;
2767   index = Xen_player_to_C_int(s7_car(args));
2768   cp = players[index]->chans[player_chans[index]];
2769   return(C_llong_to_Xen_llong(current_samples(cp)));
2770 }
2771 #endif
2772 
2773 
init_xen_player(void)2774 static void init_xen_player(void)
2775 {
2776 #if HAVE_SCHEME
2777   xen_player_tag = s7_make_c_type(s7, "<player>");
2778   s7_c_type_set_gc_free(s7, xen_player_tag, s7_xen_player_free);
2779   s7_c_type_set_is_equal(s7, xen_player_tag, s7_xen_player_is_equal);
2780   s7_c_type_set_length(s7, xen_player_tag, s7_xen_player_length);
2781   s7_c_type_set_to_string(s7, xen_player_tag, g_xen_player_to_string);
2782 #else
2783 #if HAVE_RUBY
2784   xen_player_tag = Xen_make_object_type("XenPlayer", sizeof(xen_player));
2785 #else
2786   xen_player_tag = Xen_make_object_type("Player", sizeof(xen_player));
2787 #endif
2788 #endif
2789 
2790 #if HAVE_FORTH
2791   fth_set_object_inspect(xen_player_tag,   print_xen_player);
2792   fth_set_object_dump(xen_player_tag,      g_xen_player_to_string);
2793   fth_set_object_equal(xen_player_tag,     equalp_xen_player);
2794   fth_set_object_free(xen_player_tag,      free_xen_player);
2795 #endif
2796 
2797 #if HAVE_RUBY
2798   rb_define_method(xen_player_tag, "to_s",     Xen_procedure_cast print_xen_player, 0);
2799   rb_define_method(xen_player_tag, "eql?",     Xen_procedure_cast equalp_xen_player, 1);
2800   rb_define_method(xen_player_tag, "==",       Xen_procedure_cast equalp_xen_player, 1);
2801   rb_define_method(xen_player_tag, "to_str",   Xen_procedure_cast g_xen_player_to_string, 0);
2802 #endif
2803 }
2804 
2805 /* -------------------------------------------------------------------------------- */
2806 
play_file(const char * play_name,mus_long_t start,mus_long_t end,int in_channel,int out_channel,play_process_t background,Xen stop_func)2807 static Xen play_file(const char *play_name, mus_long_t start, mus_long_t end, int in_channel, int out_channel, play_process_t background, Xen stop_func)
2808 {
2809   snd_info *sp;
2810 
2811   if (!(mus_file_probe(play_name)))
2812     return(snd_no_such_file_error(S_play, C_string_to_Xen_string(play_name)));
2813 
2814   if (!(mus_is_header_type(mus_sound_header_type(play_name))))
2815     Xen_error(BAD_HEADER,
2816 	      Xen_list_3(C_string_to_Xen_string(S_play ": ~S has unknown header: ~A"),
2817 			 C_string_to_Xen_string(play_name),
2818 			 C_string_to_Xen_string(mus_header_type_name(mus_header_type()))));
2819 
2820   if (!(mus_is_sample_type(mus_sound_sample_type(play_name))))
2821     Xen_error(Xen_make_error_type("bad-sample-type"),
2822 	      Xen_list_3(C_string_to_Xen_string(S_play ": ~S has unknown sample type: ~A"),
2823 			 C_string_to_Xen_string(play_name),
2824 			 C_string_to_Xen_string(mus_header_original_sample_type_name(mus_sound_original_sample_type(play_name), mus_sound_header_type(play_name)))));
2825   sp = make_sound_readable(play_name, false);
2826   sp->short_filename = filename_without_directory(play_name);
2827   sp->filename = NULL;
2828   sp->delete_me = (void *)1;
2829   if (in_channel != -1)
2830     play_channel_1(sp->chans[in_channel], start, end, background, 0, stop_func, (out_channel < 0) ? 0 : out_channel);
2831   else play_sound_1(sp, start, end, background, Xen_integer_zero, stop_func, S_play, -1);
2832 
2833   return(Xen_false);
2834 }
2835 
2836 #if (!HAVE_SCHEME)
2837 static Xen kw_start, kw_end, kw_channel, kw_wait, kw_edit_position, kw_stop, kw_out_channel, kw_with_sync, kw_srate, kw_channels;
2838 
init_play_keywords(void)2839 static void init_play_keywords(void)
2840 {
2841   kw_start = Xen_make_keyword("start");
2842   kw_end = Xen_make_keyword("end");
2843   kw_wait = Xen_make_keyword("wait");
2844   kw_channel = Xen_make_keyword("channel");
2845   kw_out_channel = Xen_make_keyword("out-channel");
2846   kw_edit_position = Xen_make_keyword("edit-position");
2847   kw_stop = Xen_make_keyword("stop");
2848   kw_with_sync = Xen_make_keyword("with-sync");
2849   kw_srate = Xen_make_keyword("srate");
2850   kw_channels = Xen_make_keyword("channels");
2851 }
2852 #endif
2853 
2854   #if HAVE_SCHEME
2855     #define play_example "(play \"oboe.snd\")"
2856   #endif
2857   #if HAVE_RUBY
2858     #define play_example "play(\"oboe.snd\")"
2859   #endif
2860   #if HAVE_FORTH
2861     #define play_example "\"oboe.snd\" play"
2862   #endif
2863 
2864 #define H_play "(" S_play " object start end channel edit-position out-channel with-sync wait stop srate channels): \
2865 play the object from start to end.  If channel is not given, play all channels.  If with-sync, play all objects sync'd \
2866 to the current object.  If wait, wait for the play process to finish before going on.  If out-channel, send the samples \
2867 to that DAC channel.  If edit-position, play that member of the edit list, otherwise play the current state of the object. \
2868 If stop, call that function when the play process finishes.  \
2869 If object is a string, it is assumed to be a file name: \n    " play_example "\n."
2870 
2871 #if HAVE_SCHEME
g_play(s7_scheme * sc,s7_pointer args)2872 static s7_pointer g_play(s7_scheme *sc, s7_pointer args)
2873 {
2874   s7_pointer object, p, fp;
2875   mus_long_t start, end;
2876   int channel, out_channel, srate, channels, edpos_argpos = 4, channel_argpos = 3;
2877   bool with_sync;
2878 #if (!USE_NO_GUI)
2879   bool wait;
2880 #endif
2881   s7_pointer stop_func, edit_position, channel_arg;
2882   play_process_t background;
2883   snd_info *sp;
2884 
2885   object = s7_car(args);
2886   args = s7_cdr(args);
2887 
2888   fp = s7_car(args);
2889   if (fp == Xen_false)
2890     start = 0;
2891   else
2892     {
2893       if (!s7_is_integer(fp))
2894 	return(s7_wrong_type_arg_error(sc, S_play, 1, fp, "an integer (start)"));
2895       start = s7_integer(fp);
2896       if (start < 0)
2897 	Xen_out_of_range_error(S_play, 1, fp, "start is negative?");
2898     }
2899 
2900   fp = s7_cadr(args);
2901   if (fp == Xen_false)
2902     end = NO_END_SPECIFIED;
2903   else
2904     {
2905       if (!s7_is_integer(fp))
2906 	return(s7_wrong_type_arg_error(sc, S_play, 2, fp, "an integer (end)"));
2907       end = s7_integer(fp);
2908       if (end < -1)
2909 	Xen_out_of_range_error(S_play, 2, fp, "end is negative?");
2910     }
2911 
2912   p = s7_cddr(args);
2913   fp = s7_car(p);
2914   channel_arg = fp;
2915   if (fp == Xen_false)
2916     channel = -1;
2917   else
2918     {
2919       if (!s7_is_integer(fp))
2920 	return(s7_wrong_type_arg_error(sc, S_play, 3, fp, "an integer (channel)"));
2921       channel = s7_integer(fp);
2922     }
2923 
2924   edit_position = s7_cadr(p);
2925 
2926   p = s7_cddr(p);
2927   fp = s7_car(p);
2928   if (fp == Xen_false)
2929     out_channel = -1;
2930   else
2931     {
2932       if (!s7_is_integer(fp))
2933 	return(s7_wrong_type_arg_error(sc, S_play, 5, fp, "an integer (out channel)"));
2934       out_channel = s7_integer(fp);
2935     }
2936 
2937   fp = s7_cadr(p);
2938   with_sync = (fp != Xen_false);
2939 
2940   p = s7_cddr(p);
2941 #if (!USE_NO_GUI)
2942   fp = s7_car(p);
2943   wait = (fp != Xen_false);
2944 #endif
2945 
2946   stop_func = s7_cadr(p);
2947   if ((stop_func != Xen_false) &&
2948       (!s7_is_procedure(stop_func)))
2949     return(s7_wrong_type_arg_error(sc, S_play, 8, stop_func, "a procedure (stop)"));
2950   if ((s7_is_procedure(stop_func)) &&
2951       (!s7_is_aritable(sc, stop_func, 1)))
2952     Xen_bad_arity_error(S_play, 8, fp, "stop function should take 1 argument");
2953 
2954   p = s7_cddr(p);
2955   fp = s7_car(p);
2956   if (fp == Xen_false)
2957     srate = 44100;
2958   else
2959     {
2960       if (!s7_is_integer(fp))
2961 	return(s7_wrong_type_arg_error(sc, S_play, 9, fp, "an integer (srate)"));
2962       srate = s7_integer(fp);
2963       if (srate <= 0)
2964 	Xen_out_of_range_error(S_play, 9, fp, "srate <= 0?");
2965     }
2966 
2967   fp = s7_cadr(p);
2968   if (fp == Xen_false)
2969     channels = 2;
2970   else
2971     {
2972       if (!s7_is_integer(fp))
2973 	return(s7_wrong_type_arg_error(sc, S_play, 10, fp, "an integer (channels)"));
2974       channels = s7_integer(fp);
2975       if (channels <= 0)
2976 	Xen_out_of_range_error(S_play, 10, fp, "channels <= 0?");
2977     }
2978 
2979 #else
2980 static Xen g_play(Xen arglist)
2981 {
2982   Xen object = Xen_undefined;
2983   mus_long_t start = 0, end = NO_END_SPECIFIED;
2984   int channel = -1, out_channel = -1, srate = 44100, channels = 2, edpos_argpos = 0, channel_argpos = 0;
2985   bool with_sync = false, wait = false;
2986   Xen stop_func = Xen_false, edit_position = Xen_false, channel_arg = Xen_false;
2987   play_process_t background;
2988   snd_info *sp;
2989 
2990   if (!Xen_is_null(arglist))
2991     {
2992       #define NARGS 10
2993       Xen args[NARGS * 2];
2994       Xen keys[NARGS];
2995       int orig_arg[NARGS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2996       int vals, i, arglist_len;
2997 
2998       if (!Xen_is_keyword(Xen_car(arglist)))
2999 	{
3000 	  object = Xen_car(arglist);
3001 	  arglist = Xen_cdr(arglist);
3002 	}
3003 
3004       keys[0] = kw_start;
3005       keys[1] = kw_end;
3006       keys[2] = kw_channel;
3007       keys[3] = kw_edit_position;
3008       keys[4] = kw_out_channel;
3009       keys[5] = kw_with_sync;
3010       keys[6] = kw_wait;
3011       keys[7] = kw_stop;
3012       keys[8] = kw_srate;
3013       keys[9] = kw_channels;
3014 
3015       for (i = 0; i < NARGS * 2; i++) args[i] = Xen_undefined;
3016       arglist_len = Xen_list_length(arglist);
3017       if (arglist_len > NARGS)
3018 	Xen_out_of_range_error(S_play, 0, arglist, "too many arguments");
3019 
3020       for (i = 0; i < arglist_len; i++) args[i] = Xen_list_ref(arglist, i);
3021       vals = mus_optkey_unscramble(S_play, arglist_len, NARGS, keys, args, orig_arg);
3022 
3023       if (vals > 0)
3024 	{
3025 	  start = mus_optkey_to_mus_long_t(keys[0], S_play, orig_arg[0], start);
3026 	  if (start < 0)
3027 	    Xen_out_of_range_error(S_play, orig_arg[0], keys[0], "start is negative?");
3028 
3029 	  end = mus_optkey_to_mus_long_t(keys[1], S_play, orig_arg[1], end);
3030 	  if (end < -1)
3031 	    Xen_out_of_range_error(S_play, orig_arg[1], keys[1], "end is negative?");
3032 
3033 	  channel = mus_optkey_to_int(keys[2], S_play, orig_arg[2], channel);
3034 	  channel_argpos = orig_arg[2];
3035 	  channel_arg = keys[2];
3036 
3037 	  if (!(Xen_is_keyword(keys[3])))
3038 	    {
3039 	      edit_position = keys[3];
3040 	      edpos_argpos = orig_arg[3];
3041 	    }
3042 	  out_channel = mus_optkey_to_int(keys[4], S_play, orig_arg[4], out_channel);
3043 
3044 	  with_sync = mus_optkey_to_bool(keys[5], S_play, orig_arg[5], with_sync);
3045 	  wait = mus_optkey_to_bool(keys[6], S_play, orig_arg[6], wait);
3046 	  stop_func = mus_optkey_to_procedure(keys[7], S_play, orig_arg[7], stop_func, 1, "play stop function takes 1 argument");
3047 
3048 	  srate = mus_optkey_to_int(keys[8], S_play, orig_arg[8], srate);
3049 	  if (srate <= 0)
3050 	    Xen_out_of_range_error(S_play, orig_arg[8], keys[8], "srate <= 0?");
3051 
3052 	  channels = mus_optkey_to_int(keys[9], S_play, orig_arg[9], channels);
3053 	  if (channels <= 0)
3054 	    Xen_out_of_range_error(S_play, orig_arg[9], keys[9], "channels <= 0?");
3055 	}
3056     }
3057 #endif
3058 
3059 #if USE_NO_GUI
3060   background = NOT_IN_BACKGROUND;
3061 #else
3062   if (wait) background = NOT_IN_BACKGROUND; else background = IN_BACKGROUND;
3063   /* confusing! wait=#f means don't wait for the play to complete => IN_BACKGROUND */
3064 #endif
3065   /* unspecified object means the current sound, all chans, all samps, with sync, without wait, current edpos */
3066   if (!Xen_is_bound(object))
3067     {
3068       sp = any_selected_sound();
3069       if (sp)
3070 	play_sound_1(sp, start, end, background, C_int_to_Xen_integer(AT_CURRENT_EDIT_POSITION), Xen_false, NULL, 0);
3071       return(Xen_false);
3072     }
3073 
3074   /* #f object means start sending out zeros */
3075   if (Xen_is_false(object))
3076     return(C_bool_to_Xen_boolean(add_zeros_to_play_list(srate, channels)));
3077 
3078   /* procedure object means add that function to the play list */
3079   if (Xen_is_procedure(object))
3080     return(C_bool_to_Xen_boolean(add_xen_to_play_list(object)));
3081 
3082   /* mix object */
3083   if (xen_is_mix(object))
3084     return(g_play_mix(object, start));
3085 
3086   /* selection object */
3087   if (xen_is_selection(object))
3088     {
3089       if (selection_is_active())
3090 	play_selection_1(background, stop_func);
3091       return(object);
3092     }
3093 
3094   /* region object */
3095   if (xen_is_region(object))
3096     return(g_play_region(object, background, stop_func));
3097 
3098   /* string object = filename */
3099   if (Xen_is_string(object))
3100     {
3101       char *name;
3102       name = mus_expand_filename(Xen_string_to_C_string(object));
3103       play_file((const char *)name, start, end, channel, out_channel, background, stop_func);
3104       free(name);
3105       return(object);
3106     }
3107 
3108   /* otherwise object is either a player or a sound */
3109   if (xen_is_player(object))
3110     sp = get_player_sound(object);
3111   else sp = get_sp(object);
3112 
3113   if (!sp)
3114     return(snd_no_such_sound_error(S_play, object));
3115 
3116   if ((with_sync) &&
3117       (sp->sync != 0) &&
3118       (!(is_player_sound(sp))))
3119     {
3120       sync_info *si;
3121       mus_long_t *ends = NULL;
3122       int i;
3123 
3124       si = snd_sync(sp->sync);
3125       if (end != NO_END_SPECIFIED)
3126 	{
3127 	  ends = (mus_long_t *)calloc(si->chans, sizeof(mus_long_t));
3128 	  for (i = 0; i < si->chans; i++) ends[i] = end;
3129 	}
3130       for (i = 0; i < si->chans; i++) si->begs[i] = start;
3131       play_channels_1(si->cps, si->chans, si->begs, ends, background, edit_position, false, stop_func, S_play, edpos_argpos);
3132 
3133       free_sync_info(si);
3134       if (ends) free(ends);
3135       return(Xen_false);
3136     }
3137 
3138   if (channel == -1)
3139     play_sound_1(sp, start, end, background, edit_position, stop_func, S_play, edpos_argpos);
3140   else
3141     {
3142       if ((channel < (int)(sp->nchans)) &&
3143 	  (channel >= 0))
3144 	{
3145 	  int pos;
3146 	  chan_info *cp;
3147 	  cp = sp->chans[channel];
3148 	  if (out_channel < 0) out_channel = channel;
3149 	  pos = to_c_edit_position(cp, edit_position, S_play, edpos_argpos);
3150 	  play_channel_1(cp, start, end, background, pos, stop_func, out_channel);
3151 	}
3152       else Xen_out_of_range_error(S_play, channel_argpos, channel_arg, "channel does not exist?");
3153     }
3154 
3155   return(object);
3156 }
3157 
3158 
3159 Xen no_such_player_error(const char *caller, Xen player)
3160 {
3161   Xen_error(Xen_make_error_type("no-such-player"),
3162 	    Xen_list_3(C_string_to_Xen_string("~A: no such player, ~A"),
3163 		       C_string_to_Xen_string(caller),
3164 		       player));
3165   return(Xen_false);
3166 }
3167 
3168 
3169 static Xen g_stop_playing(Xen snd)
3170 {
3171   #define H_stop_playing "(" S_stop_playing " :optional snd): stop play (DAC output) in progress"
3172   snd_info *sp = NULL;
3173 
3174   Xen_check_type(Xen_is_integer(snd) || xen_is_sound(snd) || !Xen_is_bound(snd) || xen_is_player(snd), snd, 1, S_stop_playing, "a sound or player");
3175 
3176   if (Xen_is_integer(snd) || xen_is_sound(snd))
3177     {
3178       sp = get_sp(snd);
3179       if (!sp)
3180 	return(snd_no_such_sound_error(S_stop_playing, snd));
3181     }
3182   else
3183     {
3184       if (xen_is_player(snd))
3185 	{
3186 	  sp = get_player_sound(snd);
3187 	  if (!sp)
3188 	    return(no_such_player_error(S_stop_playing, snd));
3189 	}
3190     }
3191 
3192   if (sp)
3193     stop_playing_sound(sp, PLAY_STOP_CALLED);
3194   else stop_playing_all_sounds(PLAY_STOP_CALLED);
3195 
3196   return(Xen_false);
3197 }
3198 
3199 
3200 
3201 
3202 /* add-player make-player stop-player start-playing */
3203 
3204 static Xen g_make_player(Xen snd, Xen chn)
3205 {
3206   #define H_make_player "(" S_make_player " :optional snd chn): \
3207 make a new player associated with snd's channel chn. \
3208 A player is a sort of wrapper for a channel of a sound; it supports \
3209 all the control panel functions.  Once created, you can set these \
3210 fields, then call " S_add_player " to add this channel to the list of \
3211 channels either being played (if a play is in progress) or about \
3212 to be played (via " S_start_playing ")."
3213 
3214   snd_info *true_sp, *new_sp;
3215   chan_info *cp;
3216 
3217   Snd_assert_channel(S_make_player, snd, chn, 1);
3218   true_sp = get_sp(snd);
3219   if (!true_sp)
3220     return(snd_no_such_sound_error(S_make_player, snd));
3221 
3222   cp = get_cp(snd, chn, S_make_player);
3223   new_sp = make_snd_info(NULL, "make_player:wrapper", true_sp->hdr, new_player_index(), FILE_READ_ONLY);
3224   new_sp->chans[cp->chan] = cp;
3225 
3226   return(C_int_to_Xen_player(make_player(new_sp, cp)));
3227 }
3228 
3229 
3230 static Xen g_player_home(Xen player)
3231 {
3232   #define H_player_home "(" S_player_home " player): a list of the sound index and channel number associated with player"
3233   int index;
3234 
3235   Xen_check_type(xen_is_player(player), player, 1, S_player_home, "a player");
3236   index = Xen_player_to_C_int(player);
3237 
3238   if ((index > 0) &&
3239       (index < players_size) &&
3240       (players[index]) &&
3241       (players[index]->chans) &&
3242       (player_chans[index] < (int)players[index]->nchans))
3243     {
3244       chan_info *cp;
3245       cp = players[index]->chans[player_chans[index]]; /* trying to get back to the original sound index (not the player index) */
3246 
3247       if ((cp->sound) &&
3248 	  (cp->sound->active))
3249 	return(Xen_list_2(C_int_to_Xen_sound(cp->sound->index),
3250 			  C_int_to_Xen_integer(cp->chan)));
3251       return(Xen_false); /* can this happen? */
3252     }
3253 
3254   return(no_such_player_error(S_player_home, player));
3255 }
3256 
3257 
3258 static Xen g_add_player(Xen player, Xen start, Xen end, Xen edpos, Xen stop_proc, Xen out_chan)
3259 {
3260   #define H_add_player "(" S_add_player " player :optional (start 0) (end len) (pos -1) stop-proc (out-chan player-chan)): \
3261 adds a player to the play list; the play begins when " S_start_playing " is called. \
3262 The start, end, and edit-position of the portion played can be specified.  'out-chan' is \
3263 the audio hardware output channel to use to play this channel.  It defaults to the \
3264 channel number in the sound that contains the channel being played."
3265 
3266   snd_info *sp = NULL;
3267   int index, pos;
3268   chan_info *cp;
3269   dac_info *dp = NULL;
3270   int i, ochan = -1;
3271 
3272   Xen_check_type(xen_is_player(player), player, 1, S_add_player, "a player");
3273   Xen_check_type(Xen_is_integer_or_unbound(start), start, 2, S_add_player, "an integer");
3274   Xen_check_type(Xen_is_integer_or_unbound(end), end, 3, S_add_player, "an integer");
3275   Xen_check_type(((Xen_is_procedure(stop_proc)) && (procedure_arity_ok(stop_proc, 1))) ||
3276 		  (!Xen_is_bound(stop_proc)) ||
3277 		  (Xen_is_false(stop_proc)),
3278 		  stop_proc, 5, S_add_player, "a procedure of 1 arg");
3279   Xen_check_type(Xen_is_integer_or_unbound(out_chan), out_chan, 6, S_add_player, "an integer");
3280 
3281   index = Xen_player_to_C_int(player);
3282   if ((index > 0) && (index < players_size))
3283     sp = players[index];
3284 
3285   if (!sp)
3286     return(no_such_player_error(S_add_player, player));
3287 
3288   if (play_list)
3289     for (i = 0; i < dac_max_sounds; i++)
3290       if ((play_list[i]) &&
3291 	  (sp == (play_list[i]->sp)))
3292 	Xen_error(Xen_make_error_type("arg-error"),
3293 		  Xen_list_2(C_string_to_Xen_string(S_add_player ": player ~A is already in the play list"),
3294 			     player));
3295 
3296   cp = sp->chans[player_chans[index]];
3297   pos = to_c_edit_position(cp, edpos, S_add_player, 4);
3298   if (Xen_is_integer(out_chan)) ochan = Xen_integer_to_C_int(out_chan);
3299   if (ochan < 0) ochan = cp->chan;
3300 
3301   dp = add_channel_to_play_list(cp,
3302 				sp, /* this is not cp->sound! */
3303 				beg_to_sample(start, S_add_player),
3304 				(Xen_is_llong(end)) ? Xen_llong_to_C_llong(end) : NO_END_SPECIFIED,
3305 				pos,
3306 				ochan);
3307   if (!dp) return(Xen_false);
3308 
3309   dp->stop_procedure = stop_proc;
3310   if (Xen_is_procedure(stop_proc))
3311     dp->stop_procedure_gc_loc = snd_protect(stop_proc);
3312 
3313   return(player);
3314 }
3315 
3316 
3317 static Xen g_start_playing(Xen Chans, Xen Srate, Xen In_Background)
3318 {
3319   #define H_start_playing "(" S_start_playing " :optional (chans 1) (srate 44100) (in-background " PROC_TRUE ")): \
3320 If a play-list is waiting, start it."
3321 
3322   int chans = 1, srate = 44100;
3323   bool back;
3324 
3325   Xen_check_type(Xen_is_integer_or_unbound(Chans), Chans, 1, S_start_playing, "an integer");
3326   Xen_check_type(Xen_is_integer_or_unbound(Srate), Srate, 2, S_start_playing, "an integer");
3327   Xen_check_type(Xen_is_boolean_or_unbound(In_Background), In_Background, 3, S_start_playing, "a boolean");
3328 
3329   if (Xen_is_integer(Chans)) chans = Xen_integer_to_C_int(Chans);
3330   if ((chans <= 0) || (chans > MUS_MAX_CHANS))
3331     Xen_out_of_range_error(S_start_playing, 1, Chans, "chans <= 0 or > 256?");
3332 
3333   if (Xen_is_integer(Srate)) srate = Xen_integer_to_C_int(Srate);
3334   if (srate <= 0)
3335     Xen_out_of_range_error(S_start_playing, 2, Srate, "srate <= 0?");
3336 
3337   back = Xen_boolean_to_C_bool(In_Background);
3338 
3339   start_dac(srate, chans, (back) ? IN_BACKGROUND : NOT_IN_BACKGROUND, DEFAULT_REVERB_CONTROL_DECAY);
3340   return(Xen_false);
3341 }
3342 
3343 
3344 static Xen g_stop_player(Xen player)
3345 {
3346   #define H_stop_player "(" S_stop_player " player): stop player and remove its associated sound from the current DAC playlist"
3347   snd_info *sp = NULL;
3348 
3349   Xen_check_type(xen_is_player(player), player, 1, S_stop_player, "a player");
3350 
3351   sp = get_player_sound(player);
3352   if (sp)
3353     stop_playing_sound(sp, PLAY_STOP_CALLED);
3354   return(player);
3355 }
3356 
3357 
3358 static Xen g_free_player(Xen player)
3359 {
3360   #define H_free_player "(" S_free_player " player): free all resources associated with 'player' and remove it from the current DAC playlist"
3361   snd_info *sp = NULL;
3362 
3363   Xen_check_type(xen_is_player(player), player, 1, S_free_player, "a player");
3364 
3365   sp = get_player_sound(player);
3366   if (sp)
3367     free_player_sound(sp);
3368   return(Xen_false);
3369 }
3370 
3371 
3372 /* also the dac filler needs to run on empty buffers in this case? */
3373 
3374 static Xen g_is_player(Xen obj)
3375 {
3376   #define H_is_player "(" S_is_player " obj): is 'obj' an active player"
3377   if (xen_is_player(obj))
3378     {
3379       int index;
3380       index = Xen_player_to_C_int(obj);
3381       return(C_bool_to_Xen_boolean((index > 0) &&
3382 			      (index < players_size) &&
3383 			      (players[index])));
3384     }
3385   return(Xen_false);
3386 }
3387 
3388 
3389 /* player-position? -- need quick way from index to dp to its sampler, then C_llong_to_Xen_llong(current_location(fd)) */
3390 
3391 static Xen g_players(void)
3392 {
3393   #define H_players "(" S_players ") -> list of currently active players."
3394   Xen lst = Xen_empty_list;
3395   int i;
3396   for (i = 0; i < players_size; i++)
3397     if (players[i])
3398       lst = Xen_cons(C_int_to_Xen_player(i), lst);
3399   return(lst);
3400 }
3401 
3402 
3403 static Xen g_dac_size(void) {return(C_int_to_Xen_integer(dac_size(ss)));}
3404 
3405 static Xen g_set_dac_size(Xen val)
3406 {
3407   #define H_dac_size "(" S_dac_size "): the current DAC buffer size in framples (256)"
3408   int len;
3409   Xen_check_type(Xen_is_integer(val), val, 1, S_set S_dac_size, "an integer");
3410   len = Xen_integer_to_C_int(val);
3411   if (len > 0)
3412     set_dac_size(len); /* macro in snd-0.h */
3413   return(C_int_to_Xen_integer(dac_size(ss)));
3414 }
3415 
3416 
3417 static Xen g_dac_combines_channels(void) {return(C_bool_to_Xen_boolean(dac_combines_channels(ss)));}
3418 
3419 static Xen g_set_dac_combines_channels(Xen val)
3420 {
3421   #define H_dac_combines_channels "(" S_dac_combines_channels "): " PROC_TRUE " if extra channels are to be mixed into available ones during playing. \
3422 That is, if the sound to be played has 4 channels, but the DAC can only handle 2, if this \
3423 variable is " PROC_TRUE ", the extra channels are mixed into the available ones; otherwise they are ignored."
3424 
3425   Xen_check_type(Xen_is_boolean(val), val, 1, S_set S_dac_combines_channels, "a boolean");
3426   set_dac_combines_channels(Xen_boolean_to_C_bool(val));
3427   return(C_bool_to_Xen_boolean(dac_combines_channels(ss)));
3428 }
3429 
3430 
3431 static Xen g_playing(void)
3432 {
3433   #define H_playing "(" S_playing "): " PROC_TRUE " if sound output is in progress.  If players are waiting to start, \
3434 setting this to " PROC_TRUE " starts them; setting it to " PROC_FALSE " stops output."
3435   return(C_bool_to_Xen_boolean(something_is_playing()));
3436 }
3437 
3438 
3439 static Xen g_set_playing(Xen on)
3440 {
3441   bool starting;
3442   Xen_check_type(Xen_is_boolean(on), on, 1, S_set S_playing, "a boolean");
3443   starting = Xen_boolean_to_C_bool(on);
3444   if (starting)
3445     start_dac((int)mus_srate(), 1, IN_BACKGROUND, DEFAULT_REVERB_CONTROL_DECAY); /* how to get plausible srate chans here? */
3446   else stop_playing_all_sounds(PLAY_STOP_CALLED);
3447   return(on);
3448 }
3449 
3450 
3451 static Xen g_pausing(void)
3452 {
3453   #define H_pausing "(" S_pausing "): " PROC_TRUE " if sound output is currently pausing (this can be set)."
3454   return(C_bool_to_Xen_boolean(dac_pausing));
3455 }
3456 
3457 
3458 static Xen g_set_pausing(Xen pause)
3459 {
3460   Xen_check_type(Xen_is_boolean(pause), pause, 1, S_set S_pausing, "a boolean");
3461   dac_pausing = Xen_boolean_to_C_bool(pause);
3462   play_button_pause(dac_pausing);
3463   return(pause);
3464 }
3465 
3466 
3467 static Xen g_cursor_update_interval(void) {return(C_double_to_Xen_real(cursor_update_interval(ss)));}
3468 
3469 static Xen g_set_cursor_update_interval(Xen val)
3470 {
3471   mus_float_t ctime;
3472   #define H_cursor_update_interval "(" S_cursor_update_interval "): time (seconds) between cursor updates if " S_with_tracking_cursor "."
3473 
3474   Xen_check_type(Xen_is_number(val), val, 1, S_set S_cursor_update_interval, "a number");
3475 
3476   ctime = Xen_real_to_C_double(val);
3477   if ((ctime < 0.0) || (ctime > (24 * 3600)))
3478     Xen_out_of_range_error(S_set S_cursor_update_interval, 1, val, "invalid time");
3479   set_cursor_update_interval(ctime);
3480 
3481   return(C_double_to_Xen_real(cursor_update_interval(ss)));
3482 }
3483 
3484 
3485 static Xen g_cursor_location_offset(void) {return(C_int_to_Xen_integer(cursor_location_offset(ss)));}
3486 
3487 static Xen g_set_cursor_location_offset(Xen val)
3488 {
3489   int ctime;
3490   #define H_cursor_location_offset "(" S_cursor_location_offset "): samples added to cursor location if cursor displayed during play."
3491 
3492   Xen_check_type(Xen_is_integer(val), val, 1, S_set S_cursor_location_offset, "an integer");
3493 
3494   ctime = Xen_integer_to_C_int(val);
3495   set_cursor_location_offset(ctime);
3496 
3497   return(C_int_to_Xen_integer(cursor_location_offset(ss)));
3498 }
3499 
3500 
3501 static Xen g_with_tracking_cursor(void)
3502 {
3503   #define H_with_tracking_cursor "("  S_with_tracking_cursor "): " PROC_TRUE " if cursor always moves along in waveform display as sound is played"
3504   if (with_tracking_cursor(ss) == DONT_TRACK)
3505     return(Xen_false);
3506   if (with_tracking_cursor(ss) == TRACK_AND_RETURN)
3507     return(Xen_true);
3508   return(Xen_make_keyword("track-and-stay"));
3509 }
3510 
3511 static Xen g_set_with_tracking_cursor(Xen on)
3512 {
3513   if (Xen_is_boolean(on)) /* for backwards compatibility */
3514     {
3515       set_with_tracking_cursor(ss, (Xen_boolean_to_C_bool(on)) ? TRACK_AND_RETURN : DONT_TRACK);
3516       return(on);
3517     }
3518 
3519   if (Xen_is_integer(on))
3520     {
3521       int val;
3522       val = Xen_integer_to_C_int(on);
3523       if (val == 2)
3524 	set_with_tracking_cursor(ss, TRACK_AND_STAY);
3525       else set_with_tracking_cursor(ss, ((val == 1) ? TRACK_AND_RETURN : DONT_TRACK));
3526       return(C_int_to_Xen_integer((int)with_tracking_cursor(ss)));
3527     }
3528 
3529   if (Xen_is_keyword(on))
3530     {
3531       if (Xen_keyword_is_eq(on, Xen_make_keyword("track-and-return")))
3532 	{
3533 	  set_with_tracking_cursor(ss, TRACK_AND_RETURN);
3534 	  return(on);
3535 	}
3536       if (Xen_keyword_is_eq(on, Xen_make_keyword("track-and-stay")))
3537 	{
3538 	  set_with_tracking_cursor(ss, TRACK_AND_STAY);
3539 	  return(on);
3540 	}
3541       if (Xen_keyword_is_eq(on, Xen_make_keyword("dont-track")))
3542 	{
3543 	  set_with_tracking_cursor(ss, DONT_TRACK);
3544 	  return(on);
3545 	}
3546     }
3547 
3548   Xen_check_type(false, on, 1, S_set S_with_tracking_cursor, ":dont-track, :track-and-return, or :track-and-stay");
3549   return(on);
3550 }
3551 
3552 
3553 
3554 
3555 
3556 Xen_wrap_no_args(g_with_tracking_cursor_w, g_with_tracking_cursor)
3557 Xen_wrap_1_arg(g_set_with_tracking_cursor_w, g_set_with_tracking_cursor)
3558 #if (!HAVE_SCHEME)
3559 Xen_wrap_any_args(g_play_w, g_play)
3560 #endif
3561 Xen_wrap_1_optional_arg(g_stop_playing_w, g_stop_playing)
3562 Xen_wrap_2_optional_args(g_make_player_w, g_make_player)
3563 Xen_wrap_6_optional_args(g_add_player_w, g_add_player)
3564 Xen_wrap_1_arg(g_player_home_w, g_player_home)
3565 Xen_wrap_3_optional_args(g_start_playing_w, g_start_playing)
3566 Xen_wrap_1_arg(g_stop_player_w, g_stop_player)
3567 Xen_wrap_1_arg(g_free_player_w, g_free_player)
3568 Xen_wrap_no_args(g_players_w, g_players)
3569 Xen_wrap_1_arg(g_is_player_w, g_is_player)
3570 Xen_wrap_no_args(g_dac_size_w, g_dac_size)
3571 Xen_wrap_1_arg(g_set_dac_size_w, g_set_dac_size)
3572 Xen_wrap_no_args(g_dac_combines_channels_w, g_dac_combines_channels)
3573 Xen_wrap_1_arg(g_set_dac_combines_channels_w, g_set_dac_combines_channels)
3574 Xen_wrap_no_args(g_playing_w, g_playing)
3575 Xen_wrap_1_arg(g_set_playing_w, g_set_playing)
3576 Xen_wrap_no_args(g_pausing_w, g_pausing)
3577 Xen_wrap_1_arg(g_set_pausing_w, g_set_pausing)
3578 Xen_wrap_no_args(g_cursor_update_interval_w, g_cursor_update_interval)
3579 Xen_wrap_1_arg(g_set_cursor_update_interval_w, g_set_cursor_update_interval)
3580 Xen_wrap_no_args(g_cursor_location_offset_w, g_cursor_location_offset)
3581 Xen_wrap_1_arg(g_set_cursor_location_offset_w, g_set_cursor_location_offset)
3582 
3583 #if HAVE_SCHEME
3584 static s7_pointer acc_cursor_location_offset(s7_scheme *sc, s7_pointer args) {return(g_set_cursor_location_offset(s7_cadr(args)));}
3585 static s7_pointer acc_cursor_update_interval(s7_scheme *sc, s7_pointer args) {return(g_set_cursor_update_interval(s7_cadr(args)));}
3586 static s7_pointer acc_dac_combines_channels(s7_scheme *sc, s7_pointer args) {return(g_set_dac_combines_channels(s7_cadr(args)));}
3587 static s7_pointer acc_dac_size(s7_scheme *sc, s7_pointer args) {return(g_set_dac_size(s7_cadr(args)));}
3588 static s7_pointer acc_with_tracking_cursor(s7_scheme *sc, s7_pointer args) {return(g_set_with_tracking_cursor(s7_cadr(args)));}
3589 #endif
3590 
3591 void g_init_dac(void)
3592 {
3593 #if HAVE_SCHEME
3594   s7_pointer i, b, p, t, r, pl, l, pcl_t;
3595   i = s7_make_symbol(s7, "integer?");
3596   b = s7_make_symbol(s7, "boolean?");
3597   p = s7_make_symbol(s7, "pair?");
3598   l = s7_make_symbol(s7, "list?");
3599   r = s7_make_symbol(s7, "real?");
3600   pl = s7_make_symbol(s7, "player?");
3601   t = s7_t(s7);
3602   pcl_t = s7_make_circular_signature(s7, 0, 1, t);
3603 #endif
3604 
3605   init_xen_player();
3606 #if (!HAVE_SCHEME)
3607   init_play_keywords();
3608   Xen_define_typed_procedure(S_play,           g_play_w,           0, 0, 1, H_play,          pcl_t);
3609 #else
3610   s7_define_function_star(s7, S_play, g_play, "(object #<undefined>) start end channel edit-position out-channel with-sync wait stop srate channels", H_play);
3611 #endif
3612 
3613   Xen_define_typed_procedure(S_stop_playing,   g_stop_playing_w,   0, 1, 0, H_stop_playing,  s7_make_signature(s7, 2, b, t));
3614   Xen_define_typed_procedure(S_make_player,    g_make_player_w,    0, 2, 0, H_make_player,   s7_make_signature(s7, 3, pl, t, t));
3615   Xen_define_typed_procedure(S_add_player,     g_add_player_w,     1, 5, 0, H_add_player,    s7_make_signature(s7, 7, pl, pl, i, i, t, t, i));
3616   Xen_define_typed_procedure(S_player_home,    g_player_home_w,    1, 0, 0, H_player_home,   s7_make_signature(s7, 2, p, pl));
3617   Xen_define_typed_procedure(S_start_playing,  g_start_playing_w,  0, 3, 0, H_start_playing, s7_make_signature(s7, 4, b, i, i, b));
3618   Xen_define_typed_procedure(S_stop_player,    g_stop_player_w,    1, 0, 0, H_stop_player,   s7_make_signature(s7, 2, pl, pl));
3619   Xen_define_typed_procedure(S_free_player,    g_free_player_w,    1, 0, 0, H_free_player,   s7_make_signature(s7, 2, b, pl));
3620   Xen_define_typed_procedure(S_players,        g_players_w,        0, 0, 0, H_players,       s7_make_signature(s7, 1, l));
3621   Xen_define_typed_procedure(S_is_player,      g_is_player_w,      1, 0, 0, H_is_player,     s7_make_signature(s7, 2, b, t));
3622 
3623   Xen_define_typed_dilambda(S_pausing, g_pausing_w, H_pausing,
3624 			    S_set S_pausing, g_set_pausing_w, 0, 0, 1, 0,
3625 			    s7_make_signature(s7, 1, b), s7_make_signature(s7, 2, b, b));
3626 
3627   Xen_define_typed_dilambda(S_playing, g_playing_w, H_playing,
3628 			    S_set S_playing, g_set_playing_w, 0, 0, 1, 0,
3629 			    s7_make_signature(s7, 1, b), s7_make_signature(s7, 2, b, b));
3630 
3631   Xen_define_typed_dilambda(S_with_tracking_cursor, g_with_tracking_cursor_w, H_with_tracking_cursor,
3632 			    S_set S_with_tracking_cursor, g_set_with_tracking_cursor_w, 0, 0, 1, 0,
3633 			    pcl_t, pcl_t);
3634 
3635   Xen_define_typed_dilambda(S_dac_size, g_dac_size_w, H_dac_size,
3636 			    S_set S_dac_size, g_set_dac_size_w,  0, 0, 1, 0,
3637 			    s7_make_signature(s7, 1, i), s7_make_signature(s7, 2, i, i));
3638 
3639   Xen_define_typed_dilambda(S_dac_combines_channels, g_dac_combines_channels_w, H_dac_combines_channels,
3640 			    S_set S_dac_combines_channels, g_set_dac_combines_channels_w,  0, 0, 1, 0,
3641 			    s7_make_signature(s7, 1, b), s7_make_signature(s7, 2, b, b));
3642 
3643   Xen_define_typed_dilambda(S_cursor_update_interval, g_cursor_update_interval_w, H_cursor_update_interval,
3644 			    S_set S_cursor_update_interval, g_set_cursor_update_interval_w,  0, 0, 1, 0,
3645 			    s7_make_signature(s7, 1, r), s7_make_signature(s7, 2, r, r));
3646 
3647   Xen_define_typed_dilambda(S_cursor_location_offset, g_cursor_location_offset_w, H_cursor_location_offset,
3648 			    S_set S_cursor_location_offset, g_set_cursor_location_offset_w,  0, 0, 1, 0,
3649 			    s7_make_signature(s7, 1, i), s7_make_signature(s7, 2, i, i));
3650 
3651 
3652   #define H_stop_playing_hook S_stop_playing_hook " (snd): called when a sound finishes playing."
3653   #define H_play_hook S_play_hook " (size): called each time a buffer is sent to the DAC."
3654   #define H_start_playing_hook S_start_playing_hook " (snd): called when a play request is triggered. \
3655 If it returns " PROC_TRUE ", the sound is not played."
3656 
3657   #define H_stop_playing_selection_hook S_stop_playing_selection_hook " (): called when the selection stops playing"
3658   #define H_start_playing_selection_hook S_start_playing_selection_hook " (): called when the selection starts playing"
3659 
3660   stop_playing_hook =            Xen_define_hook(S_stop_playing_hook,            "(make-hook 'snd)",  1, H_stop_playing_hook);
3661   start_playing_hook =           Xen_define_hook(S_start_playing_hook,           "(make-hook 'snd)",  1, H_start_playing_hook);
3662   play_hook =                    Xen_define_hook(S_play_hook,                    "(make-hook 'size)", 1, H_play_hook);
3663   stop_playing_selection_hook =  Xen_define_hook(S_stop_playing_selection_hook,  "(make-hook)",       0, H_stop_playing_selection_hook);
3664   start_playing_selection_hook = Xen_define_hook(S_start_playing_selection_hook, "(make-hook)",       0, H_start_playing_selection_hook);
3665 
3666 #if HAVE_SCHEME
3667   s7_set_setter(s7, ss->cursor_location_offset_symbol, s7_make_function(s7, "[acc-" S_cursor_location_offset "]", acc_cursor_location_offset, 2, 0, false, "accessor"));
3668   s7_set_setter(s7, ss->cursor_update_interval_symbol, s7_make_function(s7, "[acc-" S_cursor_update_interval "]", acc_cursor_update_interval, 2, 0, false, "accessor"));
3669   s7_set_setter(s7, ss->dac_combines_channels_symbol, s7_make_function(s7, "[acc-" S_dac_combines_channels "]", acc_dac_combines_channels, 2, 0, false, "accessor"));
3670   s7_set_setter(s7, ss->dac_size_symbol, s7_make_function(s7, "[acc-" S_dac_size "]", acc_dac_size, 2, 0, false, "accessor"));
3671   s7_set_setter(s7, ss->with_tracking_cursor_symbol, s7_make_function(s7, "[acc-" S_with_tracking_cursor "]", acc_with_tracking_cursor, 2, 0, false, "accessor"));
3672 
3673   s7_set_documentation(s7, ss->cursor_location_offset_symbol, "*cursor-location-offset*: samples added to cursor location if cursor displayed during play.");
3674   s7_set_documentation(s7, ss->cursor_update_interval_symbol, "*cursor-update-interval*: time (seconds) between cursor updates if with-tracking-cursor.");
3675   s7_set_documentation(s7, ss->dac_combines_channels_symbol, "*dac-combines-channels*: #t if extra channels are to be mixed into available ones during playing.");
3676   s7_set_documentation(s7, ss->dac_size_symbol, "*dac-size*: the current DAC buffer size in framples (256)");
3677   s7_set_documentation(s7, ss->with_tracking_cursor_symbol, "*with-tracking-cursor*: #t if cursor always moves along in waveform display as sound is played");
3678 #endif
3679 }
3680 
3681 /* dac loop [need start/end of loop in dac_info, reader goes to start when end reached (requires rebuffering)
3682  *   looper does not stop/restart -- just keep going]
3683  *   play_selection_1 could put ends somewhere, set ends to NO_END_SPECIFIED, dac_loop_sample can
3684  *   use begs/other-ends to get loop points, so free_dac_info does not need to restart the loop(?)
3685  *   If start/end selection changed while playing, are these loop points updated?
3686  */
3687