参考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);