• ALSA lib-ext plugin


    External Plugin: Filter-Type Plugin

    The filter-type plugin is a plugin to convert the PCM signals from the input and feeds to the output. Thus, this plugin always needs a slave PCM as its output.

    The plugin can modify the format and the channels of the input/output PCM. It can not modify the sample rate (because of simplicity reason).

    The following fields have to be filled in extplug record before calling snd_pcm_extplug_create() : version, name, callback. Otherfields are optional and should be initialized with zero.

    The constant SND_PCM_EXTPLUG_VERSION must be passed to the version field for the version check in alsa-lib. A non-NULL ASCII string has to be passed to the name field. The callback field contains the table of callback functions for this plugin (defined as snd_pcm_extplug_callback_t).

    The driver can set an arbitrary value (pointer) to private_data field to refer its own data in the callbacks.

    The rest fields are filled by snd_pcm_extplug_create(). The pcm field is the resultant PCM handle. The others are the current status of the PCM.

    The callback functions in snd_pcm_extplug_callback_t define the real behavior of the driver. At least, transfer callback must be given. This callback is called at each time certain size of data block is transfered to the slave PCM. Other callbacks are optional. 
    The close callback is called when the PCM is closed. If the plugin allocates private resources, this is the place to release them again. The hw_params and hw_free callbacks are called at snd_pcm_hw_params() and snd_pcm_hw_free() API calls, respectively. The last, dump callback, is called for printing the information of the given plugin.

    The init callback is called when the PCM is at prepare state or any initialization is issued. Use this callback to reset the PCM instance to a sane initial state.

    The hw_params constraints can be defined via either snd_pcm_extplug_set_param_minmax() and snd_pcm_extplug_set_param_list() functions after callingsnd_pcm_extplug_create(). The former defines the minimal and maximal acceptable values for the given hw_params parameter (SND_PCM_EXTPLUG_HW_XXX). This function can't be used for the format parameter. The latter function specifies the available parameter values as the list. As mentioned above, the rate can't be changed. Only changeable parameters are sample format and channels.

    To define the constraints of the slave PCM configuration, use either snd_pcm_extplug_set_slave_param_minmax() and snd_pcm_extplug_set_slave_param_list(). The arguments are as same as former functions.

    To clear the parameter constraints, call snd_pcm_extplug_params_reset() function.

    When using snd_pcm_extplug_set_param_*() or snd_pcm_extplug_set_slave_param_*() for any parameter. This parameter is no longer linked between the client and slave PCM. Therefore it could differ and the extplug has to support conversion between all valid parameter configurations. To keep the client and slave parameter linkedsnd_pcm_extplug_set_param_link() can be used for the corresponding parameter. For example if the extplug does not support channel nor format conversion the supported client parameters can be limited with snd_pcm_extplug_set_param_*() and afterwards #snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and #snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be called to keep the client and slave parameters the same.

    参考pcm_speex.c

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <alsa/asoundlib.h>
    #include <alsa/pcm_external.h>
    
    struct ctx_parms {
        int frames;
        int enable_dump;
        FILE *dump_fp;
        float gain;
    };
    
    typedef struct {
        snd_pcm_extplug_t ext;
        struct ctx_parms parms;
        short *buf;
        short *outbuf;
        unsigned int filled;
        unsigned int processed;
    }snd_pcm_fellowext_t;
    
    static inline void *area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset)
    {
        unsigned int bitofs = area->first + area->step * offset;
        return (char *)area->addr + bitofs / 8;
    }
    
    static void process(snd_pcm_fellowext_t *ctx)
    {
        int frames = ctx->parms.frames;
        short *inbuf = ctx->buf;
        short *outbuf= ctx->outbuf;
        int channels= ctx->ext.channels;
        int ch_idx = 0, frame_idx = 0;
        for (frame_idx = 0; frame_idx < frames; frame_idx++)
        {
            for (ch_idx = 0; ch_idx < channels; ch_idx++)
            {
                outbuf[frame_idx * channels + ch_idx] = (short)((float)inbuf[frame_idx * channels + ch_idx] * ctx->parms.gain);
            }
        }
    }
    
    static snd_pcm_sframes_t fellowext_transfer(snd_pcm_extplug_t *ext, const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset, snd_pcm_uframes_t size)
    {
        snd_pcm_fellowext_t *ctx = (snd_pcm_fellowext_t *)ext;
        short *src = area_addr(src_areas, src_offset);
        short *dst = area_addr(dst_areas, dst_offset);
        unsigned int count = size;
        int channels = ctx->ext.channels;
        int bytes_per_frame = 2 * channels;
    
        while (count > 0) {
            unsigned int chunk;
            if (ctx->filled + count > ctx->parms.frames)
                chunk = ctx->parms.frames - ctx->filled;
            else
                chunk = count;
            if (ctx->processed)
                memcpy(dst, ctx->outbuf + ctx->filled * channels, chunk * bytes_per_frame);
            else
                memset(dst, 0, chunk * bytes_per_frame);
            if (ctx->parms.enable_dump)
                fwrite(dst, 1, chunk * bytes_per_frame, ctx->parms.dump_fp);
            dst += chunk * channels;
            
            memcpy(ctx->buf + ctx->filled * channels, src, chunk * bytes_per_frame);
            ctx->filled += chunk;
            if (ctx->filled == ctx->parms.frames) {
                process(ctx);
                ctx->processed = 1;
    ctx->filled = 0; } src
    += chunk * channels; count -= chunk; } return size; } static int fellowext_init(snd_pcm_extplug_t *ext) { snd_pcm_fellowext_t *ctx = (snd_pcm_fellowext_t *)ext; int channels = ctx->ext.channels; ctx->filled = 0; ctx->processed = 0; if (!ctx->buf) { ctx->buf = malloc(ctx->parms.frames * 2 * channels); if (!ctx->buf) return -ENOMEM; } memset (ctx->buf, 0, ctx->parms.frames * 2 * channels); if (!ctx->outbuf) { ctx->outbuf = malloc(ctx->parms.frames * 2 * channels); if (!ctx->outbuf) return -ENOMEM; } memset (ctx->outbuf, 0, ctx->parms.frames * 2 * channels); return 0; } static int fellowext_close(snd_pcm_extplug_t *ext) { snd_pcm_fellowext_t *ctx = (snd_pcm_fellowext_t *)ext; if (ctx->parms.enable_dump) fclose(ctx->parms.dump_fp); if (ctx->buf) { free(ctx->buf); ctx->buf = NULL; } if (ctx->outbuf) { free(ctx->outbuf); ctx->outbuf = NULL; } return 0; } static const snd_pcm_extplug_callback_t fellowext_callback = { .transfer = fellowext_transfer, .init = fellowext_init, .close = fellowext_close, }; static int get_bool_parm(snd_config_t *n, const char *id, const char *str, int *val_ret) { int val; if (strcmp(id, str)) return 0; val = snd_config_get_bool(n); if (val < 0) { SNDERR("Invalid value for %s", id ); return val; } *val_ret = val; return 1; } static int get_int_parm(snd_config_t *n, const char *id, const char *str, int *val_ret) { long val; int err; if (strcmp(id, str)) return 0; err = snd_config_get_integer(n, &val); if (err < 0) { SNDERR("Invalid value for %s", id ); return err; } *val_ret = val; return 1; } static int get_float_parm(snd_config_t *n, const char *id, const char *str, float *val_ret) { double val; int err; if (strcmp(id, str)) return 0; err = snd_config_get_ireal(n, &val); if (err < 0) { SNDERR("Invalid value for %s", id ); return err; } *val_ret = val; return 1; } SND_PCM_PLUGIN_DEFINE_FUNC(fellowext) { snd_config_iterator_t i, next; snd_pcm_fellowext_t *ctx; snd_config_t *sconf = NULL; int err; struct ctx_parms parms = { .frames = 512, .enable_dump = 0, .dump_fp = NULL, .gain = 0.5, }; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; if (snd_config_get_id(n, &id) < 0) continue; if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) continue; if (strcmp(id, "slave") == 0) { sconf = n; continue; } err = get_int_parm(n, id, "frames", &parms.frames); if (err) goto ok; err = get_bool_parm(n, id, "enable_dump", &parms.enable_dump); if (err) goto ok; err = get_float_parm(n, id, "gain", &parms.gain); if (err) goto ok; SNDERR("Unknown field %s", id); return -EINVAL; ok: if (err < 0) return err; } if (!sconf) { SNDERR("No slave configuration for fellowext pcm");
    return -EINVAL; }
    if (parms.enable_dump) parms.dump_fp = fopen("extplug.pcm", "wb"); ctx = calloc(1, sizeof(*ctx)); if (!ctx) return -ENOMEM; ctx->ext.version = SND_PCM_EXTPLUG_VERSION; ctx->ext.name = "Fellow Ext Plugin"; ctx->ext.callback = &fellowext_callback; ctx->ext.private_data = ctx; ctx->parms = parms; err = snd_pcm_extplug_create(&ctx->ext, name, root, sconf, stream, mode); if (err < 0) { free(ctx); return err; } snd_pcm_extplug_set_param(&ctx->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 2); snd_pcm_extplug_set_slave_param(&ctx->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 2); snd_pcm_extplug_set_param(&ctx->ext, SND_PCM_EXTPLUG_HW_FORMAT, SND_PCM_FORMAT_S16); snd_pcm_extplug_set_slave_param(&ctx->ext, SND_PCM_EXTPLUG_HW_FORMAT, SND_PCM_FORMAT_S16); *pcmp = ctx->ext.pcm; return 0; } SND_PCM_PLUGIN_SYMBOL(fellowext);

     Makefile:

    ALSA_INC_PATH=/home/fellow/alsa-lib-1.2.2/output/usr/include
    ALSA_LIB_PATH=/usr/lib/i386-linux-gnu
    
    export LD_LIBRARY_PATH=${ALSA_LIB_PATH}:$LD_LIBRARY_PATH
    export CC=gcc
    export CFLAGS=-I${ALSA_INC_PATH}
    export LDFLAGS=-L{ALSA_LIB_PATH} -lasound
    SOURCE=pcm_fellowext.c
    TARGET=libasound_module_pcm_fellowext.so
    all:
        ${CC} ${SOURCE} ${CFLAGS} ${LDFLAGS} -shared -fPIC -DPIC -Wall -Werror -o ${TARGET}

    将libasound_module_pcm_fellowext.so copy到/usr/lib/alsa-lib

    在home目录下新建.asoundrc

    pcm.myout {
        type fellow
        slavepcm "default"
        dumpfile "fellowdump.pcm"
    }
    
    pcm.out1 {
        type plug
        slave.pcm myout
    }
    
    pcm.out {
        type plug
        slave.pcm "default"
    }
    
    pcm.myext {
        type fellowext
        enable_dump 1
        slave {
            pcm out1
        }
    }

    执行命令:aplay -D myext xxx.wav

  • 相关阅读:
    21.策略模式(Strategy Pattern)
    22.访问者模式(Visitor Pattern)
    23.状态模式(State Pattern)
    20.备忘录模式(Memento Pattern)
    18.中介者模式(Mediator Pattern)
    Delphi OpenDialog1 文件过滤及多选实现
    计算机存储单位/容量单位(bit、B、KB、MB、GB、TB、PB、EB、ZB、YB、BB、NB、DB、CB) 和 带宽单位 bps
    生成FTP快捷键,通过资源管理器打开的方法
    Delphi WinAPI SetJob-暂停、继续、取消或重新启动指定打印机上的打印作业。
    Delphi WinAPI FindFirstPrinterChangeNotification 和FindNextPrinterChangeNotification
  • 原文地址:https://www.cnblogs.com/fellow1988/p/12377713.html
Copyright © 2020-2023  润新知