"Fossies" - the Fresh Open Source Software Archive

Member "alsa-plugins-1.2.5/mix/pcm_upmix.c" (27 May 2021, 15818 Bytes) of package /linux/misc/alsa-plugins-1.2.5.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pcm_upmix.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.2_vs_1.2.5.

    1 /*
    2  * Automatic upmix plugin
    3  *
    4  * Copyright (c) 2006 by Takashi Iwai <tiwai@suse.de>
    5  *
    6  * This library is free software; you can redistribute it and/or modify
    7  * it under the terms of the GNU Lesser General Public License as
    8  * published by the Free Software Foundation; either version 2.1 of
    9  * the License, or (at your option) 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 Lesser General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU Lesser General Public
   17  * License along with this library; if not, write to the Free Software
   18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   19  */
   20 
   21 #include <alsa/asoundlib.h>
   22 #include <alsa/pcm_external.h>
   23 
   24 #define UPMIX_PCM_FORMAT    SND_PCM_FORMAT_S16
   25 
   26 typedef short upmix_sample_t;
   27 
   28 typedef struct snd_pcm_upmix snd_pcm_upmix_t;
   29 
   30 typedef void (*upmixer_t)(snd_pcm_upmix_t *mix,
   31               const snd_pcm_channel_area_t *dst_areas,
   32               snd_pcm_uframes_t dst_offset,
   33               const snd_pcm_channel_area_t *src_areas,
   34               snd_pcm_uframes_t src_offset,
   35               snd_pcm_uframes_t size);
   36 
   37 struct snd_pcm_upmix {
   38     snd_pcm_extplug_t ext;
   39     /* setup */
   40     int delay_ms;
   41     /* privates */
   42     upmixer_t upmix;
   43     unsigned int curpos;
   44     int delay;
   45     upmix_sample_t *delayline[2];
   46 };
   47 
   48 /* Get the current address of a channel area */
   49 static inline void *area_addr(const snd_pcm_channel_area_t *area,
   50                   snd_pcm_uframes_t offset)
   51 {
   52     unsigned int bitofs = area->first + area->step * offset;
   53     return (char *) area->addr + bitofs / 8;
   54 }
   55 
   56 /* Convert step size in bits to steps of samples */
   57 static inline unsigned int area_step(const snd_pcm_channel_area_t *area)
   58 {
   59     return area->step / 8 / sizeof(upmix_sample_t);
   60 }
   61 
   62 /* Delayed copy SL & SR */
   63 static void delayed_copy(snd_pcm_upmix_t *mix,
   64              const snd_pcm_channel_area_t *dst_areas,
   65              snd_pcm_uframes_t dst_offset,
   66              const snd_pcm_channel_area_t *src_areas,
   67              snd_pcm_uframes_t src_offset,
   68              unsigned int size)
   69 {
   70     unsigned int channel, p, delay, curpos, dst_step, src_step;
   71     upmix_sample_t *dst, *src;
   72 
   73     if (! mix->delay_ms) {
   74         snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
   75                    2, size, UPMIX_PCM_FORMAT);
   76         return;
   77     }
   78 
   79     delay = mix->delay;
   80     if (delay > size)
   81         delay = size;
   82 
   83     for (channel = 0; channel < 2; channel++) {
   84         dst = (upmix_sample_t *)area_addr(&dst_areas[channel], dst_offset);
   85         dst_step = area_step(&dst_areas[channel]);
   86         curpos = mix->curpos;
   87         for (p = 0; p < delay; p++) {
   88             *dst = mix->delayline[channel][curpos];
   89             dst += dst_step;
   90             curpos = (curpos + 1) % mix->delay;
   91         }
   92         snd_pcm_area_copy(&dst_areas[channel], dst_offset + delay,
   93                   &src_areas[channel], src_offset,
   94                   size - delay, UPMIX_PCM_FORMAT);
   95         src = (upmix_sample_t *)area_addr(&src_areas[channel],
   96                           src_offset + size - delay);
   97         src_step = area_step(&src_areas[channel]);
   98         curpos = mix->curpos;
   99         for (p = 0; p < delay; p++) {
  100             mix->delayline[channel][curpos] = *src;
  101             src += src_step;
  102             curpos = (curpos + 1) % mix->delay;
  103         }
  104     }
  105     mix->curpos = curpos;
  106 }
  107 
  108 /* Average of L+R -> C and LFE */
  109 static void average_copy(const snd_pcm_channel_area_t *dst_areas,
  110              snd_pcm_uframes_t dst_offset,
  111              const snd_pcm_channel_area_t *src_areas,
  112              snd_pcm_uframes_t src_offset,
  113              unsigned int size)
  114 {
  115     static const unsigned int nchns = 2;
  116     upmix_sample_t *dst[nchns], *src[2];
  117     unsigned int channel, dst_step[nchns], src_step[2];
  118 
  119     for (channel = 0; channel < nchns; channel++) {
  120         dst[channel] = (upmix_sample_t *)area_addr(&dst_areas[channel], dst_offset);
  121         dst_step[channel] = area_step(&dst_areas[channel]);
  122     }
  123     for (channel = 0; channel < 2; channel++) {
  124         src[channel] = (upmix_sample_t *)area_addr(&src_areas[channel], src_offset);
  125         src_step[channel] = area_step(&src_areas[channel]);
  126     }
  127     while (size--) {
  128         upmix_sample_t val = (*src[0] >> 1) + (*src[1] >> 1);
  129         for (channel = 0; channel < nchns; channel++) {
  130             *dst[channel] = val;
  131             dst[channel] += dst_step[channel];
  132         }
  133         src[0] += src_step[0];
  134         src[1] += src_step[1];
  135     }
  136 }
  137 
  138 static void upmix_1_to_71(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
  139               const snd_pcm_channel_area_t *dst_areas,
  140               snd_pcm_uframes_t dst_offset,
  141               const snd_pcm_channel_area_t *src_areas,
  142               snd_pcm_uframes_t src_offset,
  143               snd_pcm_uframes_t size)
  144 {
  145     int channel;
  146     for (channel = 0; channel < 8; channel++)
  147         snd_pcm_area_copy(&dst_areas[channel], dst_offset,
  148                   src_areas, src_offset,
  149                   size, UPMIX_PCM_FORMAT);
  150 }
  151 
  152 static void upmix_1_to_51(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
  153               const snd_pcm_channel_area_t *dst_areas,
  154               snd_pcm_uframes_t dst_offset,
  155               const snd_pcm_channel_area_t *src_areas,
  156               snd_pcm_uframes_t src_offset,
  157               snd_pcm_uframes_t size)
  158 {
  159     int channel;
  160     for (channel = 0; channel < 6; channel++)
  161         snd_pcm_area_copy(&dst_areas[channel], dst_offset,
  162                   src_areas, src_offset,
  163                   size, UPMIX_PCM_FORMAT);
  164 }
  165 
  166 static void upmix_1_to_40(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
  167               const snd_pcm_channel_area_t *dst_areas,
  168               snd_pcm_uframes_t dst_offset,
  169               const snd_pcm_channel_area_t *src_areas,
  170               snd_pcm_uframes_t src_offset,
  171               snd_pcm_uframes_t size)
  172 {
  173     int channel;
  174     for (channel = 0; channel < 4; channel++)
  175         snd_pcm_area_copy(&dst_areas[channel], dst_offset,
  176                   src_areas, src_offset,
  177                   size, UPMIX_PCM_FORMAT);
  178 }
  179 
  180 static void upmix_2_to_71(snd_pcm_upmix_t *mix,
  181               const snd_pcm_channel_area_t *dst_areas,
  182               snd_pcm_uframes_t dst_offset,
  183               const snd_pcm_channel_area_t *src_areas,
  184               snd_pcm_uframes_t src_offset,
  185               snd_pcm_uframes_t size)
  186 {
  187     snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
  188                2, size, UPMIX_PCM_FORMAT);
  189     delayed_copy(mix, dst_areas + 2, dst_offset, src_areas, src_offset, size);
  190     average_copy(dst_areas + 4, dst_offset, src_areas, src_offset, size);
  191     snd_pcm_areas_copy(dst_areas + 6, dst_offset, src_areas, src_offset,
  192                2, size, UPMIX_PCM_FORMAT);
  193     
  194 }
  195 
  196 static void upmix_2_to_51(snd_pcm_upmix_t *mix,
  197               const snd_pcm_channel_area_t *dst_areas,
  198               snd_pcm_uframes_t dst_offset,
  199               const snd_pcm_channel_area_t *src_areas,
  200               snd_pcm_uframes_t src_offset,
  201               snd_pcm_uframes_t size)
  202 {
  203     snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
  204                2, size, UPMIX_PCM_FORMAT);
  205     delayed_copy(mix, dst_areas + 2, dst_offset, src_areas, src_offset, size);
  206     average_copy(dst_areas + 4, dst_offset, src_areas, src_offset, size);
  207 }
  208 
  209 static void upmix_2_to_40(snd_pcm_upmix_t *mix,
  210               const snd_pcm_channel_area_t *dst_areas,
  211               snd_pcm_uframes_t dst_offset,
  212               const snd_pcm_channel_area_t *src_areas,
  213               snd_pcm_uframes_t src_offset,
  214               snd_pcm_uframes_t size)
  215 {
  216     snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
  217                2, size, UPMIX_PCM_FORMAT);
  218     delayed_copy(mix, dst_areas + 2, dst_offset, src_areas, src_offset, size);
  219 }
  220 
  221 static void upmix_3_to_51(snd_pcm_upmix_t *mix,
  222               const snd_pcm_channel_area_t *dst_areas,
  223               snd_pcm_uframes_t dst_offset,
  224               const snd_pcm_channel_area_t *src_areas,
  225               snd_pcm_uframes_t src_offset,
  226               snd_pcm_uframes_t size)
  227 {
  228     snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
  229                2, size, UPMIX_PCM_FORMAT);
  230     delayed_copy(mix, dst_areas + 2, dst_offset, src_areas, src_offset, size);
  231     snd_pcm_areas_copy(dst_areas + 4, dst_offset, src_areas, src_offset,
  232                2, size, UPMIX_PCM_FORMAT);
  233 }
  234 
  235 static void upmix_3_to_40(snd_pcm_upmix_t *mix,
  236               const snd_pcm_channel_area_t *dst_areas,
  237               snd_pcm_uframes_t dst_offset,
  238               const snd_pcm_channel_area_t *src_areas,
  239               snd_pcm_uframes_t src_offset,
  240               snd_pcm_uframes_t size)
  241 {
  242     snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
  243                2, size, UPMIX_PCM_FORMAT);
  244     delayed_copy(mix, dst_areas + 2, dst_offset, src_areas, src_offset, size);
  245 }
  246 
  247 static void upmix_4_to_51(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
  248               const snd_pcm_channel_area_t *dst_areas,
  249               snd_pcm_uframes_t dst_offset,
  250               const snd_pcm_channel_area_t *src_areas,
  251               snd_pcm_uframes_t src_offset,
  252               snd_pcm_uframes_t size)
  253 {
  254     snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
  255                4, size, UPMIX_PCM_FORMAT);
  256     snd_pcm_areas_copy(dst_areas + 4, dst_offset, src_areas, src_offset,
  257                2, size, UPMIX_PCM_FORMAT);
  258 }
  259 
  260 static void upmix_4_to_40(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
  261               const snd_pcm_channel_area_t *dst_areas,
  262               snd_pcm_uframes_t dst_offset,
  263               const snd_pcm_channel_area_t *src_areas,
  264               snd_pcm_uframes_t src_offset,
  265               snd_pcm_uframes_t size)
  266 {
  267     snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
  268                4, size, UPMIX_PCM_FORMAT);
  269 }
  270 
  271 static void upmix_5_to_51(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
  272               const snd_pcm_channel_area_t *dst_areas,
  273               snd_pcm_uframes_t dst_offset,
  274               const snd_pcm_channel_area_t *src_areas,
  275               snd_pcm_uframes_t src_offset,
  276               snd_pcm_uframes_t size)
  277 {
  278     snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
  279                5, size, UPMIX_PCM_FORMAT);
  280     snd_pcm_area_copy(dst_areas + 5, dst_offset, src_areas + 4, src_offset,
  281               size, UPMIX_PCM_FORMAT);
  282 }
  283 
  284 static void upmix_6_to_51(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
  285               const snd_pcm_channel_area_t *dst_areas,
  286               snd_pcm_uframes_t dst_offset,
  287               const snd_pcm_channel_area_t *src_areas,
  288               snd_pcm_uframes_t src_offset,
  289               snd_pcm_uframes_t size)
  290 {
  291     snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
  292                6, size, UPMIX_PCM_FORMAT);
  293 }
  294 
  295 static void upmix_8_to_71(snd_pcm_upmix_t *mix ATTRIBUTE_UNUSED,
  296               const snd_pcm_channel_area_t *dst_areas,
  297               snd_pcm_uframes_t dst_offset,
  298               const snd_pcm_channel_area_t *src_areas,
  299               snd_pcm_uframes_t src_offset,
  300               snd_pcm_uframes_t size)
  301 {
  302     snd_pcm_areas_copy(dst_areas, dst_offset, src_areas, src_offset,
  303                8, size, UPMIX_PCM_FORMAT);
  304 }
  305 
  306 static const upmixer_t do_upmix[8][3] = {
  307     { upmix_1_to_40, upmix_1_to_51, upmix_1_to_71 },
  308     { upmix_2_to_40, upmix_2_to_51, upmix_2_to_71 },
  309     { upmix_3_to_40, upmix_3_to_51, upmix_3_to_51 },
  310     { upmix_4_to_40, upmix_4_to_51, upmix_4_to_51 },
  311     { upmix_4_to_40, upmix_5_to_51, upmix_5_to_51 },
  312     { upmix_4_to_40, upmix_6_to_51, upmix_6_to_51 },
  313     { upmix_4_to_40, upmix_6_to_51, upmix_6_to_51 },
  314     { upmix_4_to_40, upmix_6_to_51, upmix_8_to_71 },
  315 };
  316 
  317 static snd_pcm_sframes_t
  318 upmix_transfer(snd_pcm_extplug_t *ext,
  319            const snd_pcm_channel_area_t *dst_areas,
  320            snd_pcm_uframes_t dst_offset,
  321            const snd_pcm_channel_area_t *src_areas,
  322            snd_pcm_uframes_t src_offset,
  323            snd_pcm_uframes_t size)
  324 {
  325     snd_pcm_upmix_t *mix = (snd_pcm_upmix_t *)ext;
  326     mix->upmix(mix, dst_areas, dst_offset,
  327            src_areas, src_offset, size);
  328     return size;
  329 }
  330 
  331 static int upmix_init(snd_pcm_extplug_t *ext)
  332 {
  333     snd_pcm_upmix_t *mix = (snd_pcm_upmix_t *)ext;
  334     int ctype, stype;
  335 
  336     switch (ext->slave_channels) {
  337         case 6:
  338             stype = 1;
  339             break;
  340         case 8:
  341             stype = 2;
  342             break;
  343         default:
  344             stype = 0;
  345     }
  346     ctype = ext->channels - 1;
  347     if (ctype < 0 || ctype > 7) {
  348         SNDERR("Invalid channel numbers for upmix: %d", ctype + 1);
  349         return -EINVAL;
  350     }
  351     mix->upmix = do_upmix[ctype][stype];
  352 
  353     if (mix->delay_ms) {
  354         free(mix->delayline[0]);
  355         free(mix->delayline[1]);
  356         mix->delay = ext->rate * mix->delay_ms / 1000;
  357         mix->delayline[0] = calloc(sizeof(upmix_sample_t), mix->delay);
  358         mix->delayline[1] = calloc(sizeof(upmix_sample_t), mix->delay);
  359         if (! mix->delayline[0] || ! mix->delayline[1])
  360             return -ENOMEM;
  361         mix->curpos = 0;
  362     }
  363     return 0;
  364 }
  365 
  366 static int upmix_close(snd_pcm_extplug_t *ext)
  367 {
  368     snd_pcm_upmix_t *mix = (snd_pcm_upmix_t *)ext;
  369     free(mix->delayline[0]);
  370     free(mix->delayline[1]);
  371     return 0;
  372 }
  373 
  374 #if SND_PCM_EXTPLUG_VERSION >= 0x10002
  375 static unsigned int chmap[8][8] = {
  376     { SND_CHMAP_MONO },
  377     { SND_CHMAP_FL, SND_CHMAP_FR },
  378     { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_FC },
  379     { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR },
  380     { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, SND_CHMAP_FC },
  381     { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, SND_CHMAP_FC, SND_CHMAP_LFE },
  382     { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, SND_CHMAP_FC, SND_CHMAP_LFE, SND_CHMAP_UNKNOWN },
  383     { SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_RL, SND_CHMAP_RR, SND_CHMAP_FC, SND_CHMAP_LFE, SND_CHMAP_SL, SND_CHMAP_SR },
  384 };
  385 
  386 static snd_pcm_chmap_query_t **upmix_query_chmaps(snd_pcm_extplug_t *ext ATTRIBUTE_UNUSED)
  387 {
  388     snd_pcm_chmap_query_t **maps;
  389     int i;
  390 
  391     maps = calloc(9, sizeof(void *));
  392     if (!maps)
  393         return NULL;
  394     for (i = 0; i < 8; i++) {
  395         snd_pcm_chmap_query_t *p;
  396         p = maps[i] = calloc(i + 1 + 2, sizeof(int));
  397         if (!p) {
  398             snd_pcm_free_chmaps(maps);
  399             return NULL;
  400         }
  401         p->type = SND_CHMAP_TYPE_FIXED;
  402         p->map.channels = i + 1;
  403         memcpy(p->map.pos, &chmap[i][0], (i + 1) * sizeof(int));
  404     }
  405     return maps;
  406 }
  407 
  408 static snd_pcm_chmap_t *upmix_get_chmap(snd_pcm_extplug_t *ext)
  409 {
  410     snd_pcm_chmap_t *map;
  411 
  412     if (ext->channels < 1 || ext->channels > 8)
  413         return NULL;
  414     map = malloc((ext->channels + 1) * sizeof(int));
  415     if (!map)
  416         return NULL;
  417     map->channels = ext->channels;
  418     memcpy(map->pos, &chmap[ext->channels - 1][0], ext->channels * sizeof(int));
  419     return map;
  420 }
  421 #endif /* SND_PCM_EXTPLUG_VERSION >= 0x10002 */
  422 
  423 static const snd_pcm_extplug_callback_t upmix_callback = {
  424     .transfer = upmix_transfer,
  425     .init = upmix_init,
  426     .close = upmix_close,
  427 #if SND_PCM_EXTPLUG_VERSION >= 0x10002
  428     .query_chmaps = upmix_query_chmaps,
  429     .get_chmap = upmix_get_chmap,
  430 #endif
  431 };
  432 
  433 SND_PCM_PLUGIN_DEFINE_FUNC(upmix)
  434 {
  435     snd_config_iterator_t i, next;
  436     snd_pcm_upmix_t *mix;
  437     snd_config_t *sconf = NULL;
  438     static const unsigned int chlist[3] = {4, 6, 8};
  439     unsigned int channels = 0;
  440     int delay = 10;
  441     int err;
  442 
  443     snd_config_for_each(i, next, conf) {
  444         snd_config_t *n = snd_config_iterator_entry(i);
  445         const char *id;
  446         if (snd_config_get_id(n, &id) < 0)
  447             continue;
  448         if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0)
  449             continue;
  450         if (strcmp(id, "slave") == 0) {
  451             sconf = n;
  452             continue;
  453         }
  454         if (strcmp(id, "delay") == 0) {
  455             long val;
  456             err = snd_config_get_integer(n, &val);
  457             if (err < 0) {
  458                 SNDERR("Invalid value for %s", id);
  459                 return err;
  460             }
  461             delay = val;
  462             continue;
  463         }
  464         if (strcmp(id, "channels") == 0) {
  465             long val;
  466             err = snd_config_get_integer(n, &val);
  467             if (err < 0) {
  468                 SNDERR("Invalid value for %s", id);
  469                 return err;
  470             }
  471             channels = val;
  472             if (channels != 4 && channels != 6 && channels != 0 && channels != 8) {
  473                 SNDERR("channels must be 4, 6, 8 or 0");
  474                 return -EINVAL;
  475             }
  476             continue;
  477         }
  478         SNDERR("Unknown field %s", id);
  479         return -EINVAL;
  480     }
  481 
  482     if (! sconf) {
  483         SNDERR("No slave configuration for filrmix pcm");
  484         return -EINVAL;
  485     }
  486 
  487     mix = calloc(1, sizeof(*mix));
  488     if (mix == NULL)
  489         return -ENOMEM;
  490 
  491     mix->ext.version = SND_PCM_EXTPLUG_VERSION;
  492     mix->ext.name = "Upmix Plugin";
  493     mix->ext.callback = &upmix_callback;
  494     mix->ext.private_data = mix;
  495     if (delay < 0)
  496         delay = 0;
  497     else if (delay > 1000)
  498         delay = 1000;
  499     mix->delay_ms = delay;
  500 
  501     err = snd_pcm_extplug_create(&mix->ext, name, root, sconf, stream, mode);
  502     if (err < 0) {
  503         free(mix);
  504         return err;
  505     }
  506 
  507     snd_pcm_extplug_set_param_minmax(&mix->ext,
  508                      SND_PCM_EXTPLUG_HW_CHANNELS,
  509                      1, 8);
  510     if (channels)
  511         snd_pcm_extplug_set_slave_param_minmax(&mix->ext,
  512                                SND_PCM_EXTPLUG_HW_CHANNELS,
  513                                channels, channels);
  514     else
  515         snd_pcm_extplug_set_slave_param_list(&mix->ext,
  516                              SND_PCM_EXTPLUG_HW_CHANNELS,
  517                              3, chlist);
  518     snd_pcm_extplug_set_param(&mix->ext, SND_PCM_EXTPLUG_HW_FORMAT,
  519                   UPMIX_PCM_FORMAT);
  520     snd_pcm_extplug_set_slave_param(&mix->ext, SND_PCM_EXTPLUG_HW_FORMAT,
  521                     UPMIX_PCM_FORMAT);
  522 
  523     *pcmp = mix->ext.pcm;
  524     return 0;
  525 }
  526 
  527 SND_PCM_PLUGIN_SYMBOL(upmix);