1558a398bSSimon Schubert /*- 2*2a1ad637SFrançois Tigeot * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 3558a398bSSimon Schubert * All rights reserved. 4558a398bSSimon Schubert * 5558a398bSSimon Schubert * Redistribution and use in source and binary forms, with or without 6558a398bSSimon Schubert * modification, are permitted provided that the following conditions 7558a398bSSimon Schubert * are met: 8558a398bSSimon Schubert * 1. Redistributions of source code must retain the above copyright 9558a398bSSimon Schubert * notice, this list of conditions and the following disclaimer. 10558a398bSSimon Schubert * 2. Redistributions in binary form must reproduce the above copyright 11558a398bSSimon Schubert * notice, this list of conditions and the following disclaimer in the 12558a398bSSimon Schubert * documentation and/or other materials provided with the distribution. 13558a398bSSimon Schubert * 14558a398bSSimon Schubert * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15558a398bSSimon Schubert * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16558a398bSSimon Schubert * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17558a398bSSimon Schubert * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18558a398bSSimon Schubert * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19558a398bSSimon Schubert * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20558a398bSSimon Schubert * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21558a398bSSimon Schubert * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22558a398bSSimon Schubert * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23558a398bSSimon Schubert * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24558a398bSSimon Schubert * SUCH DAMAGE. 25558a398bSSimon Schubert */ 26558a398bSSimon Schubert 27*2a1ad637SFrançois Tigeot /* feeder_volume, a long 'Lost Technology' rather than a new feature. */ 28*2a1ad637SFrançois Tigeot 29*2a1ad637SFrançois Tigeot #ifdef _KERNEL 30*2a1ad637SFrançois Tigeot #ifdef HAVE_KERNEL_OPTION_HEADERS 31*2a1ad637SFrançois Tigeot #include "opt_snd.h" 32*2a1ad637SFrançois Tigeot #endif 33558a398bSSimon Schubert #include <dev/sound/pcm/sound.h> 34*2a1ad637SFrançois Tigeot #include <dev/sound/pcm/pcm.h> 35558a398bSSimon Schubert #include "feeder_if.h" 36558a398bSSimon Schubert 37*2a1ad637SFrançois Tigeot #define SND_USE_FXDIV 38*2a1ad637SFrançois Tigeot #include "snd_fxdiv_gen.h" 39*2a1ad637SFrançois Tigeot 40*2a1ad637SFrançois Tigeot SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder_volume.c 193640 2009-06-07 19:12:08Z ariff $"); 41*2a1ad637SFrançois Tigeot #endif 42*2a1ad637SFrançois Tigeot 43*2a1ad637SFrançois Tigeot typedef void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t); 44*2a1ad637SFrançois Tigeot 45*2a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC8(s, v) (SND_VOL_CALC_SAMPLE((intpcm_t) \ 46*2a1ad637SFrançois Tigeot (s) << 8, v) >> 8) 47*2a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC16(s, v) SND_VOL_CALC_SAMPLE((intpcm_t)(s), v) 48*2a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC24(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v) 49*2a1ad637SFrançois Tigeot #define FEEDVOLUME_CALC32(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v) 50*2a1ad637SFrançois Tigeot 51*2a1ad637SFrançois Tigeot #define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN) \ 52*2a1ad637SFrançois Tigeot static void \ 53*2a1ad637SFrançois Tigeot feed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix, \ 54*2a1ad637SFrançois Tigeot uint32_t channels, uint8_t *dst, uint32_t count) \ 55*2a1ad637SFrançois Tigeot { \ 56*2a1ad637SFrançois Tigeot intpcm##BIT##_t v; \ 57*2a1ad637SFrançois Tigeot intpcm_t x; \ 58*2a1ad637SFrançois Tigeot uint32_t i; \ 59*2a1ad637SFrançois Tigeot \ 60*2a1ad637SFrançois Tigeot dst += count * PCM_##BIT##_BPS * channels; \ 61*2a1ad637SFrançois Tigeot do { \ 62*2a1ad637SFrançois Tigeot i = channels; \ 63*2a1ad637SFrançois Tigeot do { \ 64*2a1ad637SFrançois Tigeot dst -= PCM_##BIT##_BPS; \ 65*2a1ad637SFrançois Tigeot i--; \ 66*2a1ad637SFrançois Tigeot x = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ 67*2a1ad637SFrançois Tigeot v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]); \ 68*2a1ad637SFrançois Tigeot x = PCM_CLAMP_##SIGN##BIT(v); \ 69*2a1ad637SFrançois Tigeot _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \ 70*2a1ad637SFrançois Tigeot } while (i != 0); \ 71*2a1ad637SFrançois Tigeot } while (--count != 0); \ 72*2a1ad637SFrançois Tigeot } 73*2a1ad637SFrançois Tigeot 74*2a1ad637SFrançois Tigeot #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 75*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 16, LE) 76*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 32, LE) 77*2a1ad637SFrançois Tigeot #endif 78*2a1ad637SFrançois Tigeot #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 79*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 16, BE) 80*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 32, BE) 81*2a1ad637SFrançois Tigeot #endif 82*2a1ad637SFrançois Tigeot #ifdef SND_FEEDER_MULTIFORMAT 83*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 8, NE) 84*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 24, LE) 85*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(S, 24, BE) 86*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 8, NE) 87*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 16, LE) 88*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 24, LE) 89*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 32, LE) 90*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 16, BE) 91*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 24, BE) 92*2a1ad637SFrançois Tigeot FEEDVOLUME_DECLARE(U, 32, BE) 93*2a1ad637SFrançois Tigeot #endif 94*2a1ad637SFrançois Tigeot 95*2a1ad637SFrançois Tigeot struct feed_volume_info { 96*2a1ad637SFrançois Tigeot uint32_t bps, channels; 97*2a1ad637SFrançois Tigeot feed_volume_t apply; 98*2a1ad637SFrançois Tigeot int volume_class; 99*2a1ad637SFrançois Tigeot int state; 100*2a1ad637SFrançois Tigeot int matrix[SND_CHN_MAX]; 101*2a1ad637SFrançois Tigeot }; 102*2a1ad637SFrançois Tigeot 103*2a1ad637SFrançois Tigeot #define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN) \ 104*2a1ad637SFrançois Tigeot { \ 105*2a1ad637SFrançois Tigeot AFMT_##SIGN##BIT##_##ENDIAN, \ 106*2a1ad637SFrançois Tigeot feed_volume_##SIGN##BIT##ENDIAN \ 107*2a1ad637SFrançois Tigeot } 108*2a1ad637SFrançois Tigeot 109*2a1ad637SFrançois Tigeot static const struct { 110*2a1ad637SFrançois Tigeot uint32_t format; 111*2a1ad637SFrançois Tigeot feed_volume_t apply; 112*2a1ad637SFrançois Tigeot } feed_volume_info_tab[] = { 113*2a1ad637SFrançois Tigeot #if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 114*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 16, LE), 115*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 32, LE), 116*2a1ad637SFrançois Tigeot #endif 117*2a1ad637SFrançois Tigeot #if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 118*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 16, BE), 119*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 32, BE), 120*2a1ad637SFrançois Tigeot #endif 121*2a1ad637SFrançois Tigeot #ifdef SND_FEEDER_MULTIFORMAT 122*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 8, NE), 123*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 24, LE), 124*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(S, 24, BE), 125*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 8, NE), 126*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 16, LE), 127*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 24, LE), 128*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 32, LE), 129*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 16, BE), 130*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 24, BE), 131*2a1ad637SFrançois Tigeot FEEDVOLUME_ENTRY(U, 32, BE) 132*2a1ad637SFrançois Tigeot #endif 133*2a1ad637SFrançois Tigeot }; 134*2a1ad637SFrançois Tigeot 135*2a1ad637SFrançois Tigeot #define FEEDVOLUME_TAB_SIZE ((int32_t) \ 136*2a1ad637SFrançois Tigeot (sizeof(feed_volume_info_tab) / \ 137*2a1ad637SFrançois Tigeot sizeof(feed_volume_info_tab[0]))) 138558a398bSSimon Schubert 139558a398bSSimon Schubert static int 140*2a1ad637SFrançois Tigeot feed_volume_init(struct pcm_feeder *f) 141*2a1ad637SFrançois Tigeot { 142*2a1ad637SFrançois Tigeot struct feed_volume_info *info; 143*2a1ad637SFrançois Tigeot struct pcmchan_matrix *m; 144*2a1ad637SFrançois Tigeot uint32_t i; 145*2a1ad637SFrançois Tigeot int ret; 146*2a1ad637SFrançois Tigeot 147*2a1ad637SFrançois Tigeot if (f->desc->in != f->desc->out || 148*2a1ad637SFrançois Tigeot AFMT_CHANNEL(f->desc->in) > SND_CHN_MAX) 149*2a1ad637SFrançois Tigeot return (EINVAL); 150*2a1ad637SFrançois Tigeot 151*2a1ad637SFrançois Tigeot for (i = 0; i < FEEDVOLUME_TAB_SIZE; i++) { 152*2a1ad637SFrançois Tigeot if (AFMT_ENCODING(f->desc->in) == 153*2a1ad637SFrançois Tigeot feed_volume_info_tab[i].format) { 154*2a1ad637SFrançois Tigeot info = malloc(sizeof(*info), M_DEVBUF, 155*2a1ad637SFrançois Tigeot M_NOWAIT | M_ZERO); 156*2a1ad637SFrançois Tigeot if (info == NULL) 157*2a1ad637SFrançois Tigeot return (ENOMEM); 158*2a1ad637SFrançois Tigeot 159*2a1ad637SFrançois Tigeot info->bps = AFMT_BPS(f->desc->in); 160*2a1ad637SFrançois Tigeot info->channels = AFMT_CHANNEL(f->desc->in); 161*2a1ad637SFrançois Tigeot info->apply = feed_volume_info_tab[i].apply; 162*2a1ad637SFrançois Tigeot info->volume_class = SND_VOL_C_PCM; 163*2a1ad637SFrançois Tigeot info->state = FEEDVOLUME_ENABLE; 164*2a1ad637SFrançois Tigeot 165*2a1ad637SFrançois Tigeot f->data = info; 166*2a1ad637SFrançois Tigeot m = feeder_matrix_default_channel_map(info->channels); 167*2a1ad637SFrançois Tigeot if (m == NULL) { 168*2a1ad637SFrançois Tigeot free(info, M_DEVBUF); 169*2a1ad637SFrançois Tigeot return (EINVAL); 170*2a1ad637SFrançois Tigeot } 171*2a1ad637SFrançois Tigeot 172*2a1ad637SFrançois Tigeot ret = feeder_volume_apply_matrix(f, m); 173*2a1ad637SFrançois Tigeot if (ret != 0) 174*2a1ad637SFrançois Tigeot free(info, M_DEVBUF); 175*2a1ad637SFrançois Tigeot 176*2a1ad637SFrançois Tigeot return (ret); 177*2a1ad637SFrançois Tigeot } 178*2a1ad637SFrançois Tigeot } 179*2a1ad637SFrançois Tigeot 180*2a1ad637SFrançois Tigeot return (EINVAL); 181*2a1ad637SFrançois Tigeot } 182*2a1ad637SFrançois Tigeot 183*2a1ad637SFrançois Tigeot static int 184*2a1ad637SFrançois Tigeot feed_volume_free(struct pcm_feeder *f) 185*2a1ad637SFrançois Tigeot { 186*2a1ad637SFrançois Tigeot struct feed_volume_info *info; 187*2a1ad637SFrançois Tigeot 188*2a1ad637SFrançois Tigeot info = f->data; 189*2a1ad637SFrançois Tigeot if (info != NULL) 190*2a1ad637SFrançois Tigeot free(info, M_DEVBUF); 191*2a1ad637SFrançois Tigeot 192*2a1ad637SFrançois Tigeot f->data = NULL; 193*2a1ad637SFrançois Tigeot 194*2a1ad637SFrançois Tigeot return (0); 195*2a1ad637SFrançois Tigeot } 196*2a1ad637SFrançois Tigeot 197*2a1ad637SFrançois Tigeot static int 198*2a1ad637SFrançois Tigeot feed_volume_set(struct pcm_feeder *f, int what, int value) 199*2a1ad637SFrançois Tigeot { 200*2a1ad637SFrançois Tigeot struct feed_volume_info *info; 201*2a1ad637SFrançois Tigeot struct pcmchan_matrix *m; 202*2a1ad637SFrançois Tigeot int ret; 203*2a1ad637SFrançois Tigeot 204*2a1ad637SFrançois Tigeot info = f->data; 205*2a1ad637SFrançois Tigeot ret = 0; 206*2a1ad637SFrançois Tigeot 207*2a1ad637SFrançois Tigeot switch (what) { 208*2a1ad637SFrançois Tigeot case FEEDVOLUME_CLASS: 209*2a1ad637SFrançois Tigeot if (value < SND_VOL_C_BEGIN || value > SND_VOL_C_END) 210*2a1ad637SFrançois Tigeot return (EINVAL); 211*2a1ad637SFrançois Tigeot info->volume_class = value; 212*2a1ad637SFrançois Tigeot break; 213*2a1ad637SFrançois Tigeot case FEEDVOLUME_CHANNELS: 214*2a1ad637SFrançois Tigeot if (value < SND_CHN_MIN || value > SND_CHN_MAX) 215*2a1ad637SFrançois Tigeot return (EINVAL); 216*2a1ad637SFrançois Tigeot m = feeder_matrix_default_channel_map(value); 217*2a1ad637SFrançois Tigeot if (m == NULL) 218*2a1ad637SFrançois Tigeot return (EINVAL); 219*2a1ad637SFrançois Tigeot ret = feeder_volume_apply_matrix(f, m); 220*2a1ad637SFrançois Tigeot break; 221*2a1ad637SFrançois Tigeot case FEEDVOLUME_STATE: 222*2a1ad637SFrançois Tigeot if (!(value == FEEDVOLUME_ENABLE || value == FEEDVOLUME_BYPASS)) 223*2a1ad637SFrançois Tigeot return (EINVAL); 224*2a1ad637SFrançois Tigeot info->state = value; 225*2a1ad637SFrançois Tigeot break; 226*2a1ad637SFrançois Tigeot default: 227*2a1ad637SFrançois Tigeot return (EINVAL); 228*2a1ad637SFrançois Tigeot break; 229*2a1ad637SFrançois Tigeot } 230*2a1ad637SFrançois Tigeot 231*2a1ad637SFrançois Tigeot return (ret); 232*2a1ad637SFrançois Tigeot } 233*2a1ad637SFrançois Tigeot 234*2a1ad637SFrançois Tigeot static int 235*2a1ad637SFrançois Tigeot feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 236558a398bSSimon Schubert uint32_t count, void *source) 237558a398bSSimon Schubert { 238*2a1ad637SFrançois Tigeot struct feed_volume_info *info; 239*2a1ad637SFrançois Tigeot uint32_t j, align; 240*2a1ad637SFrançois Tigeot int i, *vol, *matrix; 241*2a1ad637SFrançois Tigeot uint8_t *dst; 242558a398bSSimon Schubert 243*2a1ad637SFrançois Tigeot /* 244*2a1ad637SFrançois Tigeot * Fetch filter data operation. 245*2a1ad637SFrançois Tigeot */ 246*2a1ad637SFrançois Tigeot info = f->data; 247*2a1ad637SFrançois Tigeot 248*2a1ad637SFrançois Tigeot if (info->state == FEEDVOLUME_BYPASS) 249*2a1ad637SFrançois Tigeot return (FEEDER_FEED(f->source, c, b, count, source)); 250*2a1ad637SFrançois Tigeot 251*2a1ad637SFrançois Tigeot vol = c->volume[SND_VOL_C_VAL(info->volume_class)]; 252*2a1ad637SFrançois Tigeot matrix = info->matrix; 253*2a1ad637SFrançois Tigeot 254*2a1ad637SFrançois Tigeot /* 255*2a1ad637SFrançois Tigeot * First, let see if we really need to apply gain at all. 256*2a1ad637SFrançois Tigeot */ 257*2a1ad637SFrançois Tigeot j = 0; 258*2a1ad637SFrançois Tigeot i = info->channels; 259*2a1ad637SFrançois Tigeot do { 260*2a1ad637SFrançois Tigeot if (vol[matrix[--i]] != SND_VOL_FLAT) { 261*2a1ad637SFrançois Tigeot j = 1; 262*2a1ad637SFrançois Tigeot break; 263558a398bSSimon Schubert } 264*2a1ad637SFrançois Tigeot } while (i != 0); 265*2a1ad637SFrançois Tigeot 266*2a1ad637SFrançois Tigeot /* Nope, just bypass entirely. */ 267*2a1ad637SFrançois Tigeot if (j == 0) 268*2a1ad637SFrançois Tigeot return (FEEDER_FEED(f->source, c, b, count, source)); 269*2a1ad637SFrançois Tigeot 270*2a1ad637SFrançois Tigeot dst = b; 271*2a1ad637SFrançois Tigeot align = info->bps * info->channels; 272*2a1ad637SFrançois Tigeot 273*2a1ad637SFrançois Tigeot do { 274*2a1ad637SFrançois Tigeot if (count < align) 275*2a1ad637SFrançois Tigeot break; 276*2a1ad637SFrançois Tigeot 277*2a1ad637SFrançois Tigeot j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source), 278*2a1ad637SFrançois Tigeot align); 279*2a1ad637SFrançois Tigeot if (j == 0) 280*2a1ad637SFrançois Tigeot break; 281*2a1ad637SFrançois Tigeot 282*2a1ad637SFrançois Tigeot info->apply(vol, matrix, info->channels, dst, j); 283*2a1ad637SFrançois Tigeot 284*2a1ad637SFrançois Tigeot j *= align; 285*2a1ad637SFrançois Tigeot dst += j; 286*2a1ad637SFrançois Tigeot count -= j; 287*2a1ad637SFrançois Tigeot 288*2a1ad637SFrançois Tigeot } while (count != 0); 289*2a1ad637SFrançois Tigeot 290*2a1ad637SFrançois Tigeot return (dst - b); 291558a398bSSimon Schubert } 292558a398bSSimon Schubert 293*2a1ad637SFrançois Tigeot static struct pcm_feederdesc feeder_volume_desc[] = { 294*2a1ad637SFrançois Tigeot { FEEDER_VOLUME, 0, 0, 0, 0 }, 295*2a1ad637SFrançois Tigeot { 0, 0, 0, 0, 0 } 296558a398bSSimon Schubert }; 297*2a1ad637SFrançois Tigeot 298*2a1ad637SFrançois Tigeot static kobj_method_t feeder_volume_methods[] = { 299*2a1ad637SFrançois Tigeot KOBJMETHOD(feeder_init, feed_volume_init), 300*2a1ad637SFrançois Tigeot KOBJMETHOD(feeder_free, feed_volume_free), 301*2a1ad637SFrançois Tigeot KOBJMETHOD(feeder_set, feed_volume_set), 302*2a1ad637SFrançois Tigeot KOBJMETHOD(feeder_feed, feed_volume_feed), 3037774cda2SSascha Wildner KOBJMETHOD_END 304558a398bSSimon Schubert }; 305*2a1ad637SFrançois Tigeot 306*2a1ad637SFrançois Tigeot FEEDER_DECLARE(feeder_volume, NULL); 307*2a1ad637SFrançois Tigeot 308*2a1ad637SFrançois Tigeot /* Extern */ 309*2a1ad637SFrançois Tigeot 310*2a1ad637SFrançois Tigeot /* 311*2a1ad637SFrançois Tigeot * feeder_volume_apply_matrix(): For given matrix map, apply its configuration 312*2a1ad637SFrançois Tigeot * to feeder_volume matrix structure. There are 313*2a1ad637SFrançois Tigeot * possibilites that feeder_volume be inserted 314*2a1ad637SFrançois Tigeot * before or after feeder_matrix, which in this 315*2a1ad637SFrançois Tigeot * case feeder_volume must be in a good terms 316*2a1ad637SFrançois Tigeot * with _current_ matrix. 317*2a1ad637SFrançois Tigeot */ 318*2a1ad637SFrançois Tigeot int 319*2a1ad637SFrançois Tigeot feeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m) 320*2a1ad637SFrançois Tigeot { 321*2a1ad637SFrançois Tigeot struct feed_volume_info *info; 322*2a1ad637SFrançois Tigeot uint32_t i; 323*2a1ad637SFrançois Tigeot 324*2a1ad637SFrançois Tigeot if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_VOLUME || 325*2a1ad637SFrançois Tigeot f->data == NULL || m == NULL || m->channels < SND_CHN_MIN || 326*2a1ad637SFrançois Tigeot m->channels > SND_CHN_MAX) 327*2a1ad637SFrançois Tigeot return (EINVAL); 328*2a1ad637SFrançois Tigeot 329*2a1ad637SFrançois Tigeot info = f->data; 330*2a1ad637SFrançois Tigeot 331*2a1ad637SFrançois Tigeot for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) { 332*2a1ad637SFrançois Tigeot if (i < m->channels) 333*2a1ad637SFrançois Tigeot info->matrix[i] = m->map[i].type; 334*2a1ad637SFrançois Tigeot else 335*2a1ad637SFrançois Tigeot info->matrix[i] = SND_CHN_T_FL; 336*2a1ad637SFrançois Tigeot } 337*2a1ad637SFrançois Tigeot 338*2a1ad637SFrançois Tigeot info->channels = m->channels; 339*2a1ad637SFrançois Tigeot 340*2a1ad637SFrançois Tigeot return (0); 341*2a1ad637SFrançois Tigeot } 342