ALSA lib-ext plugin

参考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, 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 (cout > 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;
        }
        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 *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_DEINE_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");
    }
    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, conf, stream, mode);
    if (err < 0) {
        free(ctx);
        return err;
    }
    snd_pcm_explug_set_param(&ctx->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 2);
    snd_pcm_explug_set_slave_param(&ctx->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 2);
    snd_pcm_explug_set_param(&ctx->ext, SND_PCM_EXTPLUG_HW_FORMAT, SND_PCM_FORMAT_S16);
    snd_pcm_explug_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);

猜你喜欢

转载自www.cnblogs.com/fellow1988/p/12377713.html