1 /* darc.lv2
2  *
3  * Copyright (C) 2018,2019 Robin Gareus <robin@gareus.org>
4  * inspired by Fons Adriaensen's zita-dc1
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef _GNU_SOURCE
21 #define _GNU_SOURCE
22 #endif
23 
24 #include <math.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "darc.h"
31 
32 #include "lv2/lv2plug.in/ns/lv2core/lv2.h"
33 
34 #ifdef DISPLAY_INTERFACE
35 #include "lv2_rgext.h"
36 #include <cairo/cairo.h>
37 #include <pango/pangocairo.h>
38 #endif
39 
40 #ifndef MAX
41 #define MAX(A, B) ((A) > (B)) ? (A) : (B)
42 #endif
43 
44 #ifndef MIN
45 #define MIN(A, B) ((A) < (B)) ? (A) : (B)
46 #endif
47 
48 /* ****************************************************************************/
49 
50 typedef struct {
51 	float sample_rate;
52 
53 	uint32_t n_channels;
54 	float    norm_input;
55 
56 	float ratio;
57 	float p_rat;
58 
59 	bool hold;
60 
61 	float igain;
62 	float p_ign;
63 	float l_ign;
64 
65 	float p_thr;
66 	float l_thr;
67 
68 	float w_att;
69 	float w_rel;
70 	float t_att;
71 	float t_rel;
72 
73 	float za1;
74 	float zr1;
75 	float zr2;
76 
77 	bool  newg;
78 	float gmax;
79 	float gmin;
80 
81 	float rms;
82 	float w_rms;
83 	float w_lpf;
84 
85 } Dyncomp;
86 
87 static inline void
Dyncomp_reset(Dyncomp * self)88 Dyncomp_reset (Dyncomp* self)
89 {
90 	self->za1  = 0.f;
91 	self->zr1  = 0.f;
92 	self->zr2  = 0.f;
93 	self->rms  = 0.f;
94 	self->gmin = 0.f;
95 	self->gmax = 0.f;
96 	self->newg = true;
97 }
98 
99 static inline void
Dyncomp_set_ratio(Dyncomp * self,float r)100 Dyncomp_set_ratio (Dyncomp* self, float r)
101 {
102 	self->p_rat = 0.5f * r;
103 }
104 
105 static inline void
Dyncomp_set_inputgain(Dyncomp * self,float g)106 Dyncomp_set_inputgain (Dyncomp* self, float g)
107 {
108 	if (g == self->l_ign) {
109 		return;
110 	}
111 	self->l_ign = g;
112 #ifdef __USE_GNU
113 	self->p_ign = exp10f (0.05f * g);
114 #else
115 	self->p_ign = powf (10.0f, 0.05f * g);
116 #endif
117 }
118 
119 static inline void
Dyncomp_set_threshold(Dyncomp * self,float t)120 Dyncomp_set_threshold (Dyncomp* self, float t)
121 {
122 	if (t == self->l_thr) {
123 		return;
124 	}
125 	self->l_thr = t;
126 	/* Note that this is signal-power, hence .5 * 10^(x/10) */
127 #ifdef __USE_GNU
128 	self->p_thr = 0.5f * exp10f (0.1f * t);
129 #else
130 	self->p_thr = 0.5f * powf (10.0f, 0.1f * t);
131 #endif
132 }
133 
134 static inline void
Dyncomp_set_hold(Dyncomp * self,bool hold)135 Dyncomp_set_hold (Dyncomp* self, bool hold)
136 {
137 	self->hold = hold;
138 }
139 
140 static inline void
Dyncomp_set_attack(Dyncomp * self,float a)141 Dyncomp_set_attack (Dyncomp* self, float a)
142 {
143 	if (a == self->t_att) {
144 		return;
145 	}
146 	self->t_att = a;
147 	self->w_att = 0.5f / (self->sample_rate * a);
148 }
149 
150 static inline void
Dyncomp_set_release(Dyncomp * self,float r)151 Dyncomp_set_release (Dyncomp* self, float r)
152 {
153 	if (r == self->t_rel) {
154 		return;
155 	}
156 	self->t_rel = r;
157 	self->w_rel = 3.5f / (self->sample_rate * r);
158 }
159 
160 static inline void
Dyncomp_get_gain(Dyncomp * self,float * gmin,float * gmax,float * rms)161 Dyncomp_get_gain (Dyncomp* self, float* gmin, float* gmax, float* rms)
162 {
163 	*gmin = self->gmin * 8.68589f; /* 20 / log(10) */
164 	*gmax = self->gmax * 8.68589f;
165 	if (self->rms > 1e-8f) {
166 		*rms = 10.f * log10f (2.f * self->rms);
167 	} else {
168 		*rms = -80;
169 	}
170 	self->newg = true;
171 }
172 
173 static inline void
Dyncomp_init(Dyncomp * self,float sample_rate,uint32_t n_channels)174 Dyncomp_init (Dyncomp* self, float sample_rate, uint32_t n_channels)
175 {
176 	self->sample_rate = sample_rate;
177 	self->n_channels  = n_channels;
178 	self->norm_input  = 1.f / n_channels;
179 
180 	self->ratio = 0.f;
181 	self->p_rat = 0.f;
182 
183 	self->igain = 1.f;
184 	self->p_ign = 1.f;
185 	self->l_ign = 0.f;
186 
187 	self->p_thr = 0.05f;
188 	self->l_thr = -10.f;
189 
190 	self->hold = false;
191 
192 	self->t_att = 0.f;
193 	self->t_rel = 0.f;
194 
195 	self->w_rms = 5.f / sample_rate;
196 	self->w_lpf = 160.f / sample_rate;
197 
198 	Dyncomp_set_attack (self, 0.01f);
199 	Dyncomp_set_release (self, 0.03f);
200 	Dyncomp_reset (self);
201 }
202 
203 static inline void
Dyncomp_process(Dyncomp * self,uint32_t n_samples,float * inp[],float * out[])204 Dyncomp_process (Dyncomp* self, uint32_t n_samples, float* inp[], float* out[])
205 {
206 	float gmin, gmax;
207 
208 	/* reset min/max gain report */
209 	if (self->newg) {
210 		gmax       = -100.0f;
211 		gmin       = 100.0f;
212 		self->newg = false;
213 	} else {
214 		gmax = self->gmax;
215 		gmin = self->gmin;
216 	}
217 
218 	/* interpolate input gain */
219 	float       g  = self->igain;
220 	const float g1 = self->p_ign;
221 	float       dg = g1 - g;
222 	if (fabsf (dg) < 1e-5f || (g > 1.f && fabsf (dg) < 1e-3f)) {
223 		g  = g1;
224 		dg = 0;
225 	}
226 
227 	/* interpolate ratio */
228 	float       r  = self->ratio;
229 	const float r1 = self->p_rat;
230 	float       dr = r1 - r;
231 	if (fabsf (dr) < 1e-5f) {
232 		r  = r1;
233 		dr = 0;
234 	}
235 
236 	/* localize variables */
237 	float za1 = self->za1;
238 	float zr1 = self->zr1;
239 	float zr2 = self->zr2;
240 
241 	float rms = self->rms;
242 
243 	const float w_rms = self->w_rms;
244 	const float w_lpf = self->w_lpf;
245 	const float w_att = self->w_att;
246 	const float w_rel = self->w_rel;
247 	const float p_thr = self->p_thr;
248 
249 	const float p_hold = self->hold ? 2.f * p_thr : 0.f;
250 
251 	const uint32_t nc  = self->n_channels;
252 	const float    n_1 = self->norm_input;
253 
254 	for (uint32_t j = 0; j < n_samples; ++j) {
255 		/* update input gain */
256 		if (dg != 0) {
257 			g += w_lpf * (g1 - g);
258 		}
259 
260 		/* Input/Key RMS */
261 		float v = 0;
262 		for (uint32_t i = 0; i < nc; ++i) {
263 			const float x = g * inp[i][j];
264 			v += x * x;
265 		}
266 
267 		v *= n_1; // normalize *= 1 / (number of channels)
268 
269 		/* slow moving RMS, used for GUI level meter display */
270 		rms += w_rms * (v - rms); // TODO: consider reporting range; 5ms integrate, 50ms min/max readout
271 
272 		/* calculate signal power relative to threshold, LPF using attack time constant */
273 		za1 += w_att * (p_thr + v - za1);
274 
275 		/* hold release */
276 		const bool hold = 0 != isless (za1, p_hold);
277 
278 		/* Note: za1 >= p_thr; so zr1, zr2 can't become denormal */
279 		if (isless (zr1, za1)) {
280 			zr1 = za1;
281 		} else if (!hold) {
282 			zr1 -= w_rel * zr1;
283 		}
284 
285 		if (isless (zr2, za1)) {
286 			zr2 = za1;
287 		} else if (!hold) {
288 			zr2 += w_rel * (zr1 - zr2);
289 		}
290 
291 		/* update ratio */
292 		if (dr != 0) {
293 			r += w_lpf * (r1 - r);
294 		}
295 
296 		/* Note: expf (a * logf (b)) == powf (b, a);
297 		 * however powf() is significantly slower
298 		 *
299 		 * Effective gain is  (zr2) ^ (-ratio).
300 		 *
301 		 * with 0 <= ratio <= 0.5 and
302 		 * zr2 being low-pass (attack/release) filtered square of the key-signal.
303 		 */
304 
305 		float pg = -r * logf (20.0f * zr2);
306 
307 		/* store min/max gain in dB, report to UI */
308 		gmax = fmaxf (gmax, pg);
309 		gmin = fminf (gmin, pg);
310 
311 		pg = g * expf (pg);
312 
313 		/* apply gain factor to all channels */
314 		for (uint32_t i = 0; i < nc; ++i) {
315 			out[i][j] = pg * inp[i][j];
316 		}
317 	}
318 
319 	/* copy back variables */
320 	self->igain = g;
321 	self->ratio = r;
322 
323 	if (!isfinite (za1)) {
324 		self->za1  = 0.f;
325 		self->zr1  = 0.f;
326 		self->zr2  = 0.f;
327 		self->newg = true; /* reset gmin/gmax next cycle */
328 	} else {
329 		self->za1  = za1;
330 		self->zr1  = zr1;
331 		self->zr2  = zr2;
332 		self->gmax = gmax;
333 		self->gmin = gmin;
334 	}
335 
336 	if (!isfinite (rms)) {
337 		self->rms = 0.f;
338 	} else if (rms > 10) {
339 		self->rms = 10; // 20dBFS
340 	} else {
341 		self->rms = rms + 1e-12; // + denormal protection
342 	}
343 }
344 
345 /* ****************************************************************************/
346 
347 typedef struct {
348 	float* _port[DARC_LAST];
349 
350 	Dyncomp dyncomp;
351 
352 	float _gmin;
353 	float _gmax;
354 	float _rms;
355 
356 	uint32_t samplecnt;
357 	uint32_t sampletme; // 50ms
358 
359 #ifdef DISPLAY_INTERFACE
360 	LV2_Inline_Display_Image_Surface surf;
361 	cairo_surface_t*                 display;
362 	LV2_Inline_Display*              queue_draw;
363 	cairo_pattern_t*                 mpat;
364 	cairo_pattern_t*                 cpat;
365 	uint32_t                         w, h;
366 	float                            ui_gmin;
367 	float                            ui_gmax;
368 #endif
369 
370 } Darc;
371 
372 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)373 instantiate (const LV2_Descriptor*     descriptor,
374              double                    rate,
375              const char*               bundle_path,
376              const LV2_Feature* const* features)
377 {
378 	Darc* self = (Darc*)calloc (1, sizeof (Darc));
379 
380 	uint32_t n_channels;
381 
382 	if (!strcmp (descriptor->URI, DARC_URI "mono")) {
383 		n_channels = 1;
384 	} else if (!strcmp (descriptor->URI, DARC_URI "stereo")) {
385 		n_channels = 2;
386 	} else {
387 		free (self);
388 		return NULL;
389 	}
390 
391 #ifdef DISPLAY_INTERFACE
392 	for (int i = 0; features[i]; ++i) {
393 		if (!strcmp (features[i]->URI, LV2_INLINEDISPLAY__queue_draw)) {
394 			self->queue_draw = (LV2_Inline_Display*)features[i]->data;
395 		}
396 	}
397 #endif
398 
399 	Dyncomp_init (&self->dyncomp, rate, n_channels);
400 	self->sampletme = ceilf (rate * 0.05); // 50ms
401 	self->samplecnt = self->sampletme;
402 
403 	return (LV2_Handle)self;
404 }
405 
406 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)407 connect_port (LV2_Handle instance,
408               uint32_t   port,
409               void*      data)
410 {
411 	Darc* self = (Darc*)instance;
412 	if (port < DARC_LAST) {
413 		self->_port[port] = (float*)data;
414 	}
415 }
416 
417 static void
activate(LV2_Handle instance)418 activate (LV2_Handle instance)
419 {
420 	Darc* self = (Darc*)instance;
421 	Dyncomp_reset (&self->dyncomp);
422 	self->samplecnt = self->sampletme;
423 }
424 
425 static void
run(LV2_Handle instance,uint32_t n_samples)426 run (LV2_Handle instance, uint32_t n_samples)
427 {
428 	Darc* self = (Darc*)instance;
429 
430 	/* bypass/enable */
431 	const bool enable = *self->_port[DARC_ENABLE] > 0;
432 
433 	if (enable) {
434 		Dyncomp_set_inputgain (&self->dyncomp, *self->_port[DARC_INPUTGAIN]);
435 		Dyncomp_set_threshold (&self->dyncomp, *self->_port[DARC_THRESHOLD]);
436 		Dyncomp_set_ratio (&self->dyncomp, *self->_port[DARC_RATIO]);
437 		Dyncomp_set_hold (&self->dyncomp, *self->_port[DARC_HOLD] > 0);
438 	} else {
439 		Dyncomp_set_inputgain (&self->dyncomp, 0);
440 		Dyncomp_set_threshold (&self->dyncomp, -10.f);
441 		Dyncomp_set_ratio (&self->dyncomp, 0);
442 		Dyncomp_set_hold (&self->dyncomp, false);
443 	}
444 
445 	Dyncomp_set_attack (&self->dyncomp, *self->_port[DARC_ATTACK]);
446 	Dyncomp_set_release (&self->dyncomp, *self->_port[DARC_RELEASE]);
447 
448 	float* ins[2]  = { self->_port[DARC_INPUT0], self->_port[DARC_INPUT1] };
449 	float* outs[2] = { self->_port[DARC_OUTPUT0], self->_port[DARC_OUTPUT1] };
450 
451 	Dyncomp_process (&self->dyncomp, n_samples, ins, outs);
452 
453 	self->samplecnt += n_samples;
454 	while (self->samplecnt >= self->sampletme) {
455 		self->samplecnt -= self->sampletme;
456 		Dyncomp_get_gain (&self->dyncomp, &self->_gmin, &self->_gmax, &self->_rms);
457 
458 		self->_gmin = fminf (40.f, fmaxf (-20.f, self->_gmin));
459 		self->_gmax = fminf (40.f, fmaxf (-20.f, self->_gmax));
460 		self->_rms  = fminf (10.f, fmaxf (-80.f, self->_rms));
461 
462 #ifdef DISPLAY_INTERFACE
463 		if (self->queue_draw && (self->ui_gmin != self->_gmin || self->ui_gmax != self->_gmax)) {
464 			self->ui_gmin = self->_gmin;
465 			self->ui_gmax = self->_gmax;
466 			self->queue_draw->queue_draw (self->queue_draw->handle);
467 		}
468 #endif
469 	}
470 
471 	*self->_port[DARC_GMIN] = self->_gmin;
472 	*self->_port[DARC_GMAX] = self->_gmax;
473 	*self->_port[DARC_RMS]  = self->_rms;
474 }
475 
476 static void
cleanup(LV2_Handle instance)477 cleanup (LV2_Handle instance)
478 {
479 	Darc* self = (Darc*)instance;
480 #ifdef DISPLAY_INTERFACE
481 	if (self->mpat) {
482 		cairo_pattern_destroy (self->mpat);
483 	}
484 	if (self->cpat) {
485 		cairo_pattern_destroy (self->cpat);
486 	}
487 	if (self->display) {
488 		cairo_surface_destroy (self->display);
489 	}
490 #endif
491 	free (instance);
492 }
493 
494 /* ****************************************************************************/
495 
496 #ifdef WITH_SIGNATURE
497 #define RTK_URI DARC_URI
498 #include "gpg_init.c"
499 #include WITH_SIGNATURE
500 struct license_info license_infos = {
501 	"x42-Compressor",
502 	"http://x42-plugins.com/x42/x42-compressor"
503 };
504 #include "gpg_lv2ext.c"
505 #endif
506 
507 #ifdef DISPLAY_INTERFACE
508 static void
create_pattern(Darc * self,const double w)509 create_pattern (Darc* self, const double w)
510 {
511 	const int x0 = floor (w * 0.05);
512 	const int x1 = ceil (w * 0.95);
513 	const int wd = x1 - x0;
514 
515 #define DEF(x) ((x0 + wd * ((x) + 20.) / 60.) / w)
516 
517 	cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, w, 0);
518 	/* clang-format off */
519 	cairo_pattern_add_color_stop_rgba (pat, 1.0,       .0, .5, .0, 0);
520 	cairo_pattern_add_color_stop_rgba (pat, DEF (40),  .0, .5, .0, 0.5);
521 	cairo_pattern_add_color_stop_rgba (pat, DEF (5),   .0, .5, .0, 0.5);
522 	cairo_pattern_add_color_stop_rgba (pat, DEF (-5),  .5, .0, .0, 0.5);
523 	cairo_pattern_add_color_stop_rgba (pat, DEF (-20), .5, .0, .0, 0.5);
524 	cairo_pattern_add_color_stop_rgba (pat, 0.0,       .5, .0, .0, 0);
525 	/* clang-format on */
526 	self->mpat = pat;
527 
528 	pat = cairo_pattern_create_linear (0.0, 0.0, w, 0);
529 	/* clang-format off */
530 	cairo_pattern_add_color_stop_rgba (pat, 1.0,       .1, .9, .1, 0);
531 	cairo_pattern_add_color_stop_rgba (pat, DEF (40),  .1, .9, .1, 1);
532 	cairo_pattern_add_color_stop_rgba (pat, DEF (5),   .1, .9, .1, 1);
533 	cairo_pattern_add_color_stop_rgba (pat, DEF (-5),  .9, .9, .1, 1);
534 	cairo_pattern_add_color_stop_rgba (pat, DEF (-20), .9, .9, .1, 1);
535 	cairo_pattern_add_color_stop_rgba (pat, 0.0,       .9, .9, .1, 0);
536 	/* clang-format on */
537 	self->cpat = pat;
538 
539 #undef DEF
540 }
541 
542 static LV2_Inline_Display_Image_Surface*
dpl_render(LV2_Handle handle,uint32_t w,uint32_t max_h)543 dpl_render (LV2_Handle handle, uint32_t w, uint32_t max_h)
544 {
545 #ifdef WITH_SIGNATURE
546 	if (!is_licensed (handle)) {
547 		return NULL;
548 	}
549 #endif
550 	uint32_t h = MAX (11, MIN (1 | (uint32_t)ceilf (w / 10.f), max_h));
551 
552 	Darc* self = (Darc*)handle;
553 
554 	if (!self->display || self->w != w || self->h != h) {
555 		if (self->display)
556 			cairo_surface_destroy (self->display);
557 		self->display = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
558 		self->w       = w;
559 		self->h       = h;
560 		if (self->mpat) {
561 			cairo_pattern_destroy (self->mpat);
562 			self->mpat = NULL;
563 		}
564 		if (self->cpat) {
565 			cairo_pattern_destroy (self->cpat);
566 			self->cpat = NULL;
567 		}
568 	}
569 
570 	if (!self->mpat || !self->cpat) {
571 		create_pattern (self, w);
572 	}
573 
574 	cairo_t* cr = cairo_create (self->display);
575 	cairo_rectangle (cr, 0, 0, w, h);
576 	cairo_set_source_rgba (cr, .2, .2, .2, 1.0);
577 	cairo_fill (cr);
578 
579 	const int x0 = floor (w * 0.05);
580 	const int x1 = ceil (w * 0.95);
581 	const int wd = x1 - x0;
582 
583 	cairo_set_line_width (cr, 1);
584 	cairo_set_source_rgba (cr, 0.8, 0.8, 0.8, 1.0);
585 
586 #define DEF(x) (rint (x0 + wd * ((x) + 20.) / 60.) - .5)
587 #define GRID(x)                         \
588 	cairo_move_to (cr, DEF (x), 0); \
589 	cairo_rel_line_to (cr, 0, h);   \
590 	cairo_stroke (cr)
591 
592 	GRID (-20);
593 	GRID (-10);
594 	GRID (0);
595 	GRID (10);
596 	GRID (20);
597 	GRID (30);
598 	GRID (40);
599 
600 #undef GRID
601 
602 	cairo_rectangle (cr, x0, 2, wd, h - 5);
603 	cairo_set_source (cr, self->mpat);
604 	cairo_fill (cr);
605 
606 	if (1) {
607 		float v0 = DEF (self->ui_gmin);
608 		float v1 = DEF (self->ui_gmax);
609 		cairo_rectangle (cr, v0 - 1, 2, 2. + v1 - v0, h - 5);
610 		cairo_set_source (cr, self->cpat);
611 		cairo_fill (cr);
612 	} else { /* bypassed */
613 		cairo_rectangle (cr, 0, 0, w, h);
614 		cairo_set_source_rgba (cr, .2, .2, .2, 0.8);
615 		cairo_fill (cr);
616 	}
617 
618 #undef DEF
619 
620 	/* finish surface */
621 	cairo_destroy (cr);
622 	cairo_surface_flush (self->display);
623 	self->surf.width  = cairo_image_surface_get_width (self->display);
624 	self->surf.height = cairo_image_surface_get_height (self->display);
625 	self->surf.stride = cairo_image_surface_get_stride (self->display);
626 	self->surf.data   = cairo_image_surface_get_data (self->display);
627 
628 	return &self->surf;
629 }
630 #endif
631 
632 const void*
extension_data(const char * uri)633 extension_data (const char* uri)
634 {
635 #ifdef DISPLAY_INTERFACE
636 	static const LV2_Inline_Display_Interface display = { dpl_render };
637 	if (!strcmp (uri, LV2_INLINEDISPLAY__interface)) {
638 #if (defined _WIN32 && defined RTK_STATIC_INIT)
639 		static int once = 0;
640 		if (!once) {
641 			once = 1;
642 			gobject_init_ctor ();
643 		}
644 #endif
645 		return &display;
646 	}
647 #endif
648 #ifdef WITH_SIGNATURE
649 	LV2_LICENSE_EXT_C
650 #endif
651 	return NULL;
652 }
653 
654 static const LV2_Descriptor descriptor_mono = {
655 	DARC_URI "mono",
656 	instantiate,
657 	connect_port,
658 	activate,
659 	run,
660 	NULL,
661 	cleanup,
662 	extension_data
663 };
664 
665 static const LV2_Descriptor descriptor_stereo = {
666 	DARC_URI "stereo",
667 	instantiate,
668 	connect_port,
669 	activate,
670 	run,
671 	NULL,
672 	cleanup,
673 	extension_data
674 };
675 
676 /* clang-format off */
677 #undef LV2_SYMBOL_EXPORT
678 #ifdef _WIN32
679 # define LV2_SYMBOL_EXPORT __declspec(dllexport)
680 #else
681 # define LV2_SYMBOL_EXPORT __attribute__ ((visibility ("default")))
682 #endif
683 /* clang-format on */
684 LV2_SYMBOL_EXPORT
685 const LV2_Descriptor*
lv2_descriptor(uint32_t index)686 lv2_descriptor (uint32_t index)
687 {
688 	switch (index) {
689 		case 0:
690 			return &descriptor_mono;
691 		case 1:
692 			return &descriptor_stereo;
693 		default:
694 			return NULL;
695 	}
696 }
697