From 78dc424b72d639b4b58c6ef86351fea00eb774b0 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 2 Dec 1999 14:31:26 +0000 Subject: [PATCH] PCM plugin patches made by Abramo Bagnara. Added new plugin - voice & balance. --- include/pcm.h | 32 ++- src/pcm/pcm_plugin.c | 405 +++++++++++++++++++----------------- src/pcm/plugin/Makefile.am | 2 +- src/pcm/plugin/adpcm.c | 20 +- src/pcm/plugin/alaw.c | 20 +- src/pcm/plugin/interleave.c | 17 +- src/pcm/plugin/linear.c | 24 ++- src/pcm/plugin/mulaw.c | 20 +- src/pcm/plugin/rate.c | 84 ++++++-- src/pcm/plugin/voices.c | 33 +-- src/pcm/plugin/volbal.c | 171 +++++++++++++++ 11 files changed, 574 insertions(+), 254 deletions(-) create mode 100644 src/pcm/plugin/volbal.c diff --git a/include/pcm.h b/include/pcm.h index 00f8c3a2..13b8c9da 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -122,17 +122,31 @@ int snd_pcm_plugin_build_stream(snd_pcm_t *handle, int channel, snd_pcm_plugin_t int snd_pcm_plugin_build_block(snd_pcm_t *handle, int channel, snd_pcm_plugin_t **r_plugin); int snd_pcm_plugin_build_mmap(snd_pcm_t *handle, int channel, snd_pcm_plugin_t **r_plugin); /* conversion plugins */ -int snd_pcm_plugin_build_interleave(int src_interleave, int dst_interleave, int format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_linear(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_mulaw(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_alaw(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_adpcm(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_rate(int src_format, int src_rate, int src_voices, - int dst_format, int dst_rate, int dst_voices, +int snd_pcm_plugin_build_interleave(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_mulaw(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_alaw(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_adpcm(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_rate(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_voices(int src_format, int src_voices, - int dst_format, int dst_voices, +int snd_pcm_plugin_build_voices(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin); +int snd_pcm_plugin_build_volbal(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + int *ttable, + snd_pcm_plugin_t **r_plugin); /* * Loopback interface diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index ce3cc174..7f54f235 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -285,123 +285,111 @@ static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action) return 0; } -static void swap_formats(int channel, int *src, int *dst) -{ - int tmp; - - if (channel == SND_PCM_CHANNEL_PLAYBACK) - return; - tmp = *src; - *src = *dst; - *dst = tmp; -} - -#define CONVERT_RATIO(dest, ratio) dest = ((int)(((double)dest * ratio)+0.5)) - int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) { - double ratio; - snd_pcm_channel_params_t sparams; - snd_pcm_channel_info_t sinfo; + snd_pcm_channel_params_t hwparams; + snd_pcm_channel_params_t srcparams, tmpparams; + snd_pcm_channel_params_t *dstparams; + snd_pcm_channel_info_t hwinfo; snd_pcm_plugin_t *plugin; - int err, srcfmt, dstfmt; + int err; if (!pcm || !params || params->channel < 0 || params->channel > 1) return -EINVAL; - memcpy(&sparams, params, sizeof(sparams)); + memcpy(&hwparams, params, sizeof(hwparams)); /* * try to decide, if a conversion is required */ - memset(&sinfo, 0, sizeof(sinfo)); - sinfo.channel = params->channel; - if ((err = snd_pcm_channel_info(pcm, &sinfo)) < 0) { + memset(&hwinfo, 0, sizeof(hwinfo)); + hwinfo.channel = params->channel; + if ((err = snd_pcm_channel_info(pcm, &hwinfo)) < 0) { snd_pcm_plugin_clear(pcm, params->channel); return err; } - if ((sinfo.formats & (1 << sparams.format.format)) == 0) { - if ((snd_pcm_plugin_formats(pcm, sinfo.formats) & (1 << sparams.format.format)) == 0) + if ((hwinfo.formats & (1 << params->format.format)) == 0) { + if ((snd_pcm_plugin_formats(pcm, hwinfo.formats) & (1 << params->format.format)) == 0) return -EINVAL; - switch (sparams.format.format) { + switch (params->format.format) { case SND_PCM_SFMT_U8: - if (sinfo.formats & SND_PCM_FMT_S8) { - sparams.format.format = SND_PCM_SFMT_S8; - } else if (sinfo.formats & SND_PCM_FMT_U16_LE) { - sparams.format.format = SND_PCM_SFMT_U16_LE; - } else if (sinfo.formats & SND_PCM_FMT_S16_LE) { - sparams.format.format = SND_PCM_SFMT_S16_LE; + if (hwinfo.formats & SND_PCM_FMT_S8) { + hwparams.format.format = SND_PCM_SFMT_S8; + } else if (hwinfo.formats & SND_PCM_FMT_U16_LE) { + hwparams.format.format = SND_PCM_SFMT_U16_LE; + } else if (hwinfo.formats & SND_PCM_FMT_S16_LE) { + hwparams.format.format = SND_PCM_SFMT_S16_LE; } else { return -EINVAL; } break; case SND_PCM_SFMT_S8: - if (sinfo.formats & SND_PCM_FMT_U8) { - sparams.format.format = SND_PCM_SFMT_U8; - } else if (sinfo.formats & SND_PCM_FMT_S16_LE) { - sparams.format.format = SND_PCM_SFMT_S16_LE; - } else if (sinfo.formats & SND_PCM_FMT_U16_LE) { - sparams.format.format = SND_PCM_SFMT_U16_LE; + if (hwinfo.formats & SND_PCM_FMT_U8) { + hwparams.format.format = SND_PCM_SFMT_U8; + } else if (hwinfo.formats & SND_PCM_FMT_S16_LE) { + hwparams.format.format = SND_PCM_SFMT_S16_LE; + } else if (hwinfo.formats & SND_PCM_FMT_U16_LE) { + hwparams.format.format = SND_PCM_SFMT_U16_LE; } else { return -EINVAL; } break; case SND_PCM_SFMT_S16_LE: - if (sinfo.formats & SND_PCM_FMT_U16_LE) { - sparams.format.format = SND_PCM_SFMT_U16_LE; - } else if (sinfo.formats & SND_PCM_FMT_S8) { - sparams.format.format = SND_PCM_SFMT_S8; - } else if (sinfo.formats & SND_PCM_FMT_U8) { - sparams.format.format = SND_PCM_SFMT_U8; + if (hwinfo.formats & SND_PCM_FMT_U16_LE) { + hwparams.format.format = SND_PCM_SFMT_U16_LE; + } else if (hwinfo.formats & SND_PCM_FMT_S8) { + hwparams.format.format = SND_PCM_SFMT_S8; + } else if (hwinfo.formats & SND_PCM_FMT_U8) { + hwparams.format.format = SND_PCM_SFMT_U8; } else { return -EINVAL; } break; case SND_PCM_SFMT_U16_LE: - if (sinfo.formats & SND_PCM_FMT_S16_LE) { - sparams.format.format = SND_PCM_SFMT_S16_LE; - } else if (sinfo.formats & SND_PCM_FMT_U8) { - sparams.format.format = SND_PCM_SFMT_U8; - } else if (sinfo.formats & SND_PCM_FMT_S8) { - sparams.format.format = SND_PCM_SFMT_S8; + if (hwinfo.formats & SND_PCM_FMT_S16_LE) { + hwparams.format.format = SND_PCM_SFMT_S16_LE; + } else if (hwinfo.formats & SND_PCM_FMT_U8) { + hwparams.format.format = SND_PCM_SFMT_U8; + } else if (hwinfo.formats & SND_PCM_FMT_S8) { + hwparams.format.format = SND_PCM_SFMT_S8; } else { return -EINVAL; } break; case SND_PCM_SFMT_MU_LAW: - if (sinfo.formats & SND_PCM_FMT_S16_LE) { - sparams.format.format = SND_PCM_SFMT_S16_LE; - } else if (sinfo.formats & SND_PCM_FMT_U16_LE) { - sparams.format.format = SND_PCM_SFMT_U16_LE; - } else if (sinfo.formats & SND_PCM_FMT_S8) { - sparams.format.format = SND_PCM_SFMT_S8; - } else if (sinfo.formats & SND_PCM_FMT_U8) { - sparams.format.format = SND_PCM_SFMT_U8; + if (hwinfo.formats & SND_PCM_FMT_S16_LE) { + hwparams.format.format = SND_PCM_SFMT_S16_LE; + } else if (hwinfo.formats & SND_PCM_FMT_U16_LE) { + hwparams.format.format = SND_PCM_SFMT_U16_LE; + } else if (hwinfo.formats & SND_PCM_FMT_S8) { + hwparams.format.format = SND_PCM_SFMT_S8; + } else if (hwinfo.formats & SND_PCM_FMT_U8) { + hwparams.format.format = SND_PCM_SFMT_U8; } else { return -EINVAL; } break; case SND_PCM_SFMT_A_LAW: - if (sinfo.formats & SND_PCM_FMT_S16_LE) { - sparams.format.format = SND_PCM_SFMT_S16_LE; - } else if (sinfo.formats & SND_PCM_FMT_U16_LE) { - sparams.format.format = SND_PCM_SFMT_U16_LE; - } else if (sinfo.formats & SND_PCM_FMT_S8) { - sparams.format.format = SND_PCM_SFMT_S8; - } else if (sinfo.formats & SND_PCM_FMT_U8) { - sparams.format.format = SND_PCM_SFMT_U8; + if (hwinfo.formats & SND_PCM_FMT_S16_LE) { + hwparams.format.format = SND_PCM_SFMT_S16_LE; + } else if (hwinfo.formats & SND_PCM_FMT_U16_LE) { + hwparams.format.format = SND_PCM_SFMT_U16_LE; + } else if (hwinfo.formats & SND_PCM_FMT_S8) { + hwparams.format.format = SND_PCM_SFMT_S8; + } else if (hwinfo.formats & SND_PCM_FMT_U8) { + hwparams.format.format = SND_PCM_SFMT_U8; } else { return -EINVAL; } case SND_PCM_SFMT_IMA_ADPCM: - if (sinfo.formats & SND_PCM_FMT_S16_LE) { - sparams.format.format = SND_PCM_SFMT_S16_LE; - } else if (sinfo.formats & SND_PCM_FMT_U16_LE) { - sparams.format.format = SND_PCM_SFMT_U16_LE; - } else if (sinfo.formats & SND_PCM_FMT_S8) { - sparams.format.format = SND_PCM_SFMT_S8; - } else if (sinfo.formats & SND_PCM_FMT_U8) { - sparams.format.format = SND_PCM_SFMT_U8; + if (hwinfo.formats & SND_PCM_FMT_S16_LE) { + hwparams.format.format = SND_PCM_SFMT_S16_LE; + } else if (hwinfo.formats & SND_PCM_FMT_U16_LE) { + hwparams.format.format = SND_PCM_SFMT_U16_LE; + } else if (hwinfo.formats & SND_PCM_FMT_S8) { + hwparams.format.format = SND_PCM_SFMT_S8; + } else if (hwinfo.formats & SND_PCM_FMT_U8) { + hwparams.format.format = SND_PCM_SFMT_U8; } else { return -EINVAL; } @@ -411,25 +399,58 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) } } + /* voices */ + if (params->format.voices < hwinfo.min_voices || + params->format.voices > hwinfo.max_voices) { + int dst_voices = params->format.voices < hwinfo.min_voices ? + hwinfo.min_voices : hwinfo.max_voices; + if ((params->format.rate < hwinfo.min_rate || + params->format.rate > hwinfo.max_rate) && + dst_voices > 2) + dst_voices = 2; + hwparams.format.voices = dst_voices; + } + + /* rate */ + if (params->format.rate < hwinfo.min_rate || + params->format.rate > hwinfo.max_rate) { + int dst_rate = params->format.rate < hwinfo.min_rate ? + hwinfo.min_rate : hwinfo.max_rate; + hwparams.format.rate = dst_rate; + } + + /* interleave */ + hwparams.format.interleave = params->format.interleave; + if (!(hwinfo.flags & SND_PCM_CHNINFO_INTERLEAVE)) + hwparams.format.interleave = 0; + if (!(hwinfo.flags & SND_PCM_CHNINFO_NONINTERLEAVE)) + hwparams.format.interleave = 1; + /* * add necessary plugins */ snd_pcm_plugin_clear(pcm, params->channel); - if (sparams.format.voices < sinfo.min_voices || - sparams.format.voices > sinfo.max_voices) { - int src_voices = sparams.format.voices; - int dst_voices = sparams.format.voices < sinfo.min_voices ? - sinfo.min_voices : sinfo.max_voices; - if (sparams.format.rate < sinfo.min_rate || - sparams.format.rate > sinfo.max_rate) - dst_voices = 2; - sparams.format.voices = dst_voices; - swap_formats(params->channel, &src_voices, &dst_voices); - err = snd_pcm_plugin_build_voices(params->format.format, src_voices, - params->format.format, dst_voices, - &plugin); + if (params->channel == SND_PCM_CHANNEL_PLAYBACK) { + memcpy(&srcparams, params, sizeof(srcparams)); + memcpy(&tmpparams, params, sizeof(tmpparams)); + dstparams = &hwparams; + } else { + memcpy(&srcparams, &hwparams, sizeof(srcparams)); + memcpy(&tmpparams, &hwparams, sizeof(tmpparams)); + dstparams = params; + } + + /* Convert to interleaved format if needed */ + if (!srcparams.format.interleave && + (srcparams.format.voices != dstparams->format.voices || + (srcparams.format.rate != dstparams->format.rate && + srcparams.format.voices > 1))) { + tmpparams.format.interleave = 1; + err = snd_pcm_plugin_build_interleave(&srcparams.format, + &tmpparams.format, + &plugin); if (err < 0) { snd_pcm_plugin_free(plugin); return err; @@ -439,64 +460,69 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) snd_pcm_plugin_free(plugin); return err; } + srcparams.format.interleave = 1; + /* Avoid useless interleave revert */ + if (params->channel == SND_PCM_CHANNEL_PLAYBACK && + (hwinfo.flags & SND_PCM_CHNINFO_INTERLEAVE)) + dstparams->format.interleave = 1; } - /* - * formats - */ - - if (params->format.format == sparams.format.format) - goto __format_skip; - /* build additional plugins for conversion */ - switch (params->format.format) { - case SND_PCM_SFMT_MU_LAW: - srcfmt = SND_PCM_SFMT_MU_LAW; - dstfmt = sparams.format.format; - swap_formats(params->channel, &srcfmt, &dstfmt); - err = snd_pcm_plugin_build_mulaw(srcfmt, dstfmt, &plugin); - break; - case SND_PCM_SFMT_A_LAW: - srcfmt = SND_PCM_SFMT_A_LAW; - dstfmt = sparams.format.format; - swap_formats(params->channel, &srcfmt, &dstfmt); - err = snd_pcm_plugin_build_alaw(srcfmt, dstfmt, &plugin); - break; - case SND_PCM_SFMT_IMA_ADPCM: - srcfmt = SND_PCM_SFMT_IMA_ADPCM; - dstfmt = sparams.format.format; - swap_formats(params->channel, &srcfmt, &dstfmt); - err = snd_pcm_plugin_build_adpcm(srcfmt, dstfmt, &plugin); - break; - default: - srcfmt = params->format.format; - dstfmt = sparams.format.format; - swap_formats(params->channel, &srcfmt, &dstfmt); - err = snd_pcm_plugin_build_linear(srcfmt, dstfmt, &plugin); - } - if (err < 0) - return err; - err = snd_pcm_plugin_append(pcm, params->channel, plugin); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - - __format_skip: + /* voices reduction */ + if (srcparams.format.voices > dstparams->format.voices) { + tmpparams.format.voices = dstparams->format.voices; + err = snd_pcm_plugin_build_voices(&srcparams.format, + &tmpparams.format, + &plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(pcm, params->channel, plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcparams.format.voices = dstparams->format.voices; + } - /* - * rate - */ + /* format change */ + if (srcparams.format.format != dstparams->format.format) { + switch (params->format.format) { + case SND_PCM_SFMT_MU_LAW: + err = snd_pcm_plugin_build_mulaw(&srcparams.format, + &tmpparams.format, + &plugin); + break; + case SND_PCM_SFMT_A_LAW: + err = snd_pcm_plugin_build_alaw(&srcparams.format, + &tmpparams.format, + &plugin); + break; + case SND_PCM_SFMT_IMA_ADPCM: + err = snd_pcm_plugin_build_adpcm(&srcparams.format, + &tmpparams.format, + &plugin); + break; + default: + err = snd_pcm_plugin_build_linear(&srcparams.format, + &tmpparams.format, + &plugin); + } + if (err < 0) + return err; + err = snd_pcm_plugin_append(pcm, params->channel, plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + } - if (sparams.format.rate < sinfo.min_rate || - sparams.format.rate > sinfo.max_rate) { - int src_rate = sparams.format.rate; - int dst_rate = sparams.format.rate < sinfo.min_rate ? - sinfo.min_rate : sinfo.max_rate; - sparams.format.rate = dst_rate; - swap_formats(params->channel, &src_rate, &dst_rate); - err = snd_pcm_plugin_build_rate(sparams.format.format, src_rate, sparams.format.voices, - sparams.format.format, dst_rate, sparams.format.voices, - &plugin); + /* rate resampling */ + if (srcparams.format.rate != dstparams->format.rate) { + tmpparams.format.rate = dstparams->format.rate; + err = snd_pcm_plugin_build_rate(&srcparams.format, + &tmpparams.format, + &plugin); if (err < 0) { snd_pcm_plugin_free(plugin); return err; @@ -506,46 +532,55 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) snd_pcm_plugin_free(plugin); return err; } + srcparams.format.rate = dstparams->format.rate; } - /* - * interleave - */ - - if (params->format.voices > 1 && sinfo.mode == SND_PCM_MODE_BLOCK) { - int src_interleave, dst_interleave; - - if (params->format.interleave) { - src_interleave = dst_interleave = 1; - if (!(sinfo.flags & SND_PCM_CHNINFO_INTERLEAVE)) - dst_interleave = 0; - } else { - src_interleave = dst_interleave = 0; - if (!(sinfo.flags & SND_PCM_CHNINFO_NONINTERLEAVE)) - dst_interleave = 1; + /* voices extension */ + if (srcparams.format.voices < dstparams->format.voices) { + tmpparams.format.voices = dstparams->format.voices; + err = snd_pcm_plugin_build_voices(&srcparams.format, + &tmpparams.format, + &plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(pcm, params->channel, plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; } - if (src_interleave != dst_interleave) { - sparams.format.interleave = dst_interleave; - swap_formats(params->channel, &src_interleave, &dst_interleave); - err = snd_pcm_plugin_build_interleave(src_interleave, dst_interleave, sparams.format.format, &plugin); - if (err < 0) - return err; - err = snd_pcm_plugin_append(pcm, params->channel, plugin); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } + srcparams.format.voices = dstparams->format.voices; + } + + /* interleave change */ + if (params->format.voices > 1 && + hwinfo.mode == SND_PCM_MODE_BLOCK && + srcparams.format.interleave != dstparams->format.interleave) { + tmpparams.format.interleave = dstparams->format.interleave; + err = snd_pcm_plugin_build_interleave(&srcparams.format, + &tmpparams.format, + &plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + err = snd_pcm_plugin_append(pcm, params->channel, plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; } + srcparams.format.interleave = dstparams->format.interleave; } /* * I/O plugins */ - if (sinfo.mode == SND_PCM_MODE_STREAM) { + if (hwinfo.mode == SND_PCM_MODE_STREAM) { err = snd_pcm_plugin_build_stream(pcm, params->channel, &plugin); - } else if (sinfo.mode == SND_PCM_MODE_BLOCK) { - if (sinfo.flags & SND_PCM_CHNINFO_MMAP) { + } else if (hwinfo.mode == SND_PCM_MODE_BLOCK) { + if (hwinfo.flags & SND_PCM_CHNINFO_MMAP) { err = snd_pcm_plugin_build_mmap(pcm, params->channel, &plugin); } else { err = snd_pcm_plugin_build_block(pcm, params->channel, &plugin); @@ -561,25 +596,19 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) return err; } - /* - * ratio - */ - - ratio = snd_pcm_plugin_hardware_ratio(pcm, params->channel); - if (ratio <= 0) - return -EINVAL; + /* compute right sizes */ if (params->mode == SND_PCM_MODE_STREAM) { - CONVERT_RATIO(sparams.buf.stream.queue_size, ratio); - CONVERT_RATIO(sparams.buf.stream.max_fill, ratio); + hwparams.buf.stream.queue_size = snd_pcm_plugin_hardware_size(pcm, hwparams.channel, hwparams.buf.stream.queue_size); + hwparams.buf.stream.max_fill = snd_pcm_plugin_hardware_size(pcm, hwparams.channel, hwparams.buf.stream.max_fill); } else if (params->mode == SND_PCM_MODE_BLOCK) { - CONVERT_RATIO(sparams.buf.block.frag_size, ratio); + hwparams.buf.block.frag_size = snd_pcm_plugin_hardware_size(pcm, hwparams.channel, hwparams.buf.block.frag_size); } else { return -EINVAL; } - err = snd_pcm_channel_params(pcm, &sparams); + err = snd_pcm_channel_params(pcm, &hwparams); if (err < 0) return err; - err = snd_pcm_plugin_action(pcm, sparams.channel, INIT); + err = snd_pcm_plugin_action(pcm, hwparams.channel, INIT); if (err < 0) return err; return 0; @@ -587,7 +616,6 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) int snd_pcm_plugin_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) { - double ratio; int err; if (!pcm || !setup || setup->channel < 0 || setup->channel > 1) @@ -595,13 +623,10 @@ int snd_pcm_plugin_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) err = snd_pcm_channel_setup(pcm, setup); if (err < 0) return err; - ratio = snd_pcm_plugin_transfer_ratio(pcm, setup->channel); - if (ratio <= 0) - return -EINVAL; if (setup->mode == SND_PCM_MODE_STREAM) { - CONVERT_RATIO(setup->buf.stream.queue_size, ratio); + setup->buf.stream.queue_size = snd_pcm_plugin_transfer_size(pcm, setup->channel, setup->buf.stream.queue_size); } else if (setup->mode == SND_PCM_MODE_BLOCK) { - CONVERT_RATIO(setup->buf.block.frag_size, ratio); + setup->buf.block.frag_size = snd_pcm_plugin_transfer_size(pcm, setup->channel, setup->buf.block.frag_size); } else { return -EINVAL; } @@ -621,9 +646,9 @@ int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status) ratio = snd_pcm_plugin_transfer_ratio(pcm, status->channel); if (ratio <= 0) return -EINVAL; - CONVERT_RATIO(status->scount, ratio); - CONVERT_RATIO(status->count, ratio); - CONVERT_RATIO(status->free, ratio); + status->scount = snd_pcm_plugin_transfer_size(pcm, status->channel, status->scount); + status->count = snd_pcm_plugin_transfer_size(pcm, status->channel, status->count); + status->free = snd_pcm_plugin_transfer_size(pcm, status->channel, status->free); return 0; } @@ -790,7 +815,7 @@ ssize_t snd_pcm_plugin_write(snd_pcm_t *pcm, const void *buffer, size_t count) } if (src_ptr1) snd_pcm_plugin_alloc_unlock(pcm, src_ptr1); - plugin = plugin->next; + plugin = next; src_ptr = dst_ptr; src_ptr1 = dst_ptr1; dst_ptr1 = NULL; diff --git a/src/pcm/plugin/Makefile.am b/src/pcm/plugin/Makefile.am index 0902e656..6fcd7371 100644 --- a/src/pcm/plugin/Makefile.am +++ b/src/pcm/plugin/Makefile.am @@ -1,7 +1,7 @@ EXTRA_LTLIBRARIES = libpcmplugin.la libpcmplugin_la_SOURCES = block.c mmap.c stream.c linear.c interleave.c \ - mulaw.c alaw.c adpcm.c rate.c voices.c + mulaw.c alaw.c adpcm.c rate.c voices.c volbal.c all: libpcmplugin.la diff --git a/src/pcm/plugin/adpcm.c b/src/pcm/plugin/adpcm.c index ef6a1dfe..accc705c 100644 --- a/src/pcm/plugin/adpcm.c +++ b/src/pcm/plugin/adpcm.c @@ -880,17 +880,25 @@ static ssize_t adpcm_dst_size(snd_pcm_plugin_t *plugin, size_t size) } } -int snd_pcm_plugin_build_adpcm(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin) +int snd_pcm_plugin_build_adpcm(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) { struct adpcm_private_data *data; snd_pcm_plugin_t *plugin; combination_t cmd; - if (!r_plugin) + if (!r_plugin || !src_format || !dst_format) return -EINVAL; *r_plugin = NULL; - if (dst_format == SND_PCM_SFMT_IMA_ADPCM) { - switch (src_format) { + + if (src_format->interleave != dst_format->interleave || + src_format->voices != dst_format->voices || + src_format->rate != dst_format->rate) + return -EINVAL; + + if (dst_format->format == SND_PCM_SFMT_IMA_ADPCM) { + switch (src_format->format) { case SND_PCM_SFMT_U8: cmd = _U8_ADPCM; break; case SND_PCM_SFMT_S8: cmd = _S8_ADPCM; break; case SND_PCM_SFMT_U16_LE: cmd = _U16LE_ADPCM; break; @@ -900,8 +908,8 @@ int snd_pcm_plugin_build_adpcm(int src_format, int dst_format, snd_pcm_plugin_t default: return -EINVAL; } - } else if (src_format == SND_PCM_SFMT_IMA_ADPCM) { - switch (dst_format) { + } else if (src_format->format == SND_PCM_SFMT_IMA_ADPCM) { + switch (dst_format->format) { case SND_PCM_SFMT_U8: cmd = _ADPCM_U8; break; case SND_PCM_SFMT_S8: cmd = _ADPCM_S8; break; case SND_PCM_SFMT_U16_LE: cmd = _ADPCM_U16LE; break; diff --git a/src/pcm/plugin/alaw.c b/src/pcm/plugin/alaw.c index 0f1ef8bb..9dbe3ba4 100644 --- a/src/pcm/plugin/alaw.c +++ b/src/pcm/plugin/alaw.c @@ -414,7 +414,9 @@ static ssize_t alaw_dst_size(snd_pcm_plugin_t *plugin, size_t size) } } -int snd_pcm_plugin_build_alaw(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin) +int snd_pcm_plugin_build_alaw(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) { struct alaw_private_data *data; snd_pcm_plugin_t *plugin; @@ -423,8 +425,16 @@ int snd_pcm_plugin_build_alaw(int src_format, int dst_format, snd_pcm_plugin_t * if (!r_plugin) return -EINVAL; *r_plugin = NULL; - if (dst_format == SND_PCM_SFMT_A_LAW) { - switch (src_format) { + + if (src_format->interleave != dst_format->interleave) + return -EINVAL; + if (src_format->rate != dst_format->rate) + return -EINVAL; + if (src_format->voices != dst_format->voices) + return -EINVAL; + + if (dst_format->format == SND_PCM_SFMT_A_LAW) { + switch (src_format->format) { case SND_PCM_SFMT_U8: cmd = _U8_ALAW; break; case SND_PCM_SFMT_S8: cmd = _S8_ALAW; break; case SND_PCM_SFMT_U16_LE: cmd = _U16LE_ALAW; break; @@ -434,8 +444,8 @@ int snd_pcm_plugin_build_alaw(int src_format, int dst_format, snd_pcm_plugin_t * default: return -EINVAL; } - } else if (src_format == SND_PCM_SFMT_A_LAW) { - switch (dst_format) { + } else if (src_format->format == SND_PCM_SFMT_A_LAW) { + switch (dst_format->format) { case SND_PCM_SFMT_U8: cmd = _ALAW_U8; break; case SND_PCM_SFMT_S8: cmd = _ALAW_S8; break; case SND_PCM_SFMT_U16_LE: cmd = _ALAW_U16LE; break; diff --git a/src/pcm/plugin/interleave.c b/src/pcm/plugin/interleave.c index 164beeb5..2038ff36 100644 --- a/src/pcm/plugin/interleave.c +++ b/src/pcm/plugin/interleave.c @@ -177,7 +177,9 @@ static ssize_t interleave_transfer(snd_pcm_plugin_t *plugin, return src_size; } -int snd_pcm_plugin_build_interleave(int src_interleave, int dst_interleave, int format, snd_pcm_plugin_t **r_plugin) +int snd_pcm_plugin_build_interleave(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) { struct interleave_private_data *data; snd_pcm_plugin_t *plugin; @@ -186,14 +188,21 @@ int snd_pcm_plugin_build_interleave(int src_interleave, int dst_interleave, int if (!r_plugin) return -EINVAL; - if (src_interleave && !dst_interleave) { + if (src_format->interleave && !dst_format->interleave) { cmd = _INTERLEAVE_NON; - } else if (!src_interleave && dst_interleave) { + } else if (!src_format->interleave && dst_format->interleave) { cmd = _NON_INTERLEAVE; } else { return -EINVAL; } - switch (format) { + if (src_format->format != dst_format->format) + return -EINVAL; + if (src_format->rate != dst_format->rate) + return -EINVAL; + if (src_format->voices != dst_format->voices) + return -EINVAL; + + switch (dst_format->format) { case SND_PCM_SFMT_S8: case SND_PCM_SFMT_U8: size = 1; break; case SND_PCM_SFMT_S16_LE: diff --git a/src/pcm/plugin/linear.c b/src/pcm/plugin/linear.c index d3b74e4e..23f7dc22 100644 --- a/src/pcm/plugin/linear.c +++ b/src/pcm/plugin/linear.c @@ -310,7 +310,9 @@ static int linear_sign(int format) } } -int snd_pcm_plugin_build_linear(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin) +int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) { struct linear_private_data *data; snd_pcm_plugin_t *plugin; @@ -320,12 +322,20 @@ int snd_pcm_plugin_build_linear(int src_format, int dst_format, snd_pcm_plugin_t if (!r_plugin) return -EINVAL; *r_plugin = NULL; - wide1 = linear_wide(src_format); - endian1 = linear_endian(src_format); - sign1 = linear_sign(src_format); - wide2 = linear_wide(dst_format); - endian2 = linear_endian(dst_format); - sign2 = linear_sign(dst_format); + + if (src_format->interleave != dst_format->interleave) + return -EINVAL; + if (src_format->rate != dst_format->rate) + return -EINVAL; + if (src_format->voices != dst_format->voices) + return -EINVAL; + + wide1 = linear_wide(src_format->format); + endian1 = linear_endian(src_format->format); + sign1 = linear_sign(src_format->format); + wide2 = linear_wide(dst_format->format); + endian2 = linear_endian(dst_format->format); + sign2 = linear_sign(dst_format->format); if (wide1 < 0 || wide2 < 0 || endian1 < 0 || endian2 < 0 || sign1 < 0 || sign2 < 0) return -EINVAL; #if __BYTE_ORDER == __LITTLE_ENDIAN diff --git a/src/pcm/plugin/mulaw.c b/src/pcm/plugin/mulaw.c index 7c04910e..095752bd 100644 --- a/src/pcm/plugin/mulaw.c +++ b/src/pcm/plugin/mulaw.c @@ -424,7 +424,9 @@ static ssize_t mulaw_dst_size(snd_pcm_plugin_t *plugin, size_t size) } } -int snd_pcm_plugin_build_mulaw(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin) +int snd_pcm_plugin_build_mulaw(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + snd_pcm_plugin_t **r_plugin) { struct mulaw_private_data *data; snd_pcm_plugin_t *plugin; @@ -433,8 +435,16 @@ int snd_pcm_plugin_build_mulaw(int src_format, int dst_format, snd_pcm_plugin_t if (!r_plugin) return -EINVAL; *r_plugin = NULL; - if (dst_format == SND_PCM_SFMT_MU_LAW) { - switch (src_format) { + + if (src_format->interleave != dst_format->interleave) + return -EINVAL; + if (src_format->rate != dst_format->rate) + return -EINVAL; + if (src_format->voices != dst_format->voices) + return -EINVAL; + + if (dst_format->format == SND_PCM_SFMT_MU_LAW) { + switch (src_format->format) { case SND_PCM_SFMT_U8: cmd = _U8_MULAW; break; case SND_PCM_SFMT_S8: cmd = _S8_MULAW; break; case SND_PCM_SFMT_U16_LE: cmd = _U16LE_MULAW; break; @@ -444,8 +454,8 @@ int snd_pcm_plugin_build_mulaw(int src_format, int dst_format, snd_pcm_plugin_t default: return -EINVAL; } - } else if (src_format == SND_PCM_SFMT_MU_LAW) { - switch (dst_format) { + } else if (src_format->format == SND_PCM_SFMT_MU_LAW) { + switch (dst_format->format) { case SND_PCM_SFMT_U8: cmd = _MULAW_U8; break; case SND_PCM_SFMT_S8: cmd = _MULAW_S8; break; case SND_PCM_SFMT_U16_LE: cmd = _MULAW_U16LE; break; diff --git a/src/pcm/plugin/rate.c b/src/pcm/plugin/rate.c index fa1ee0a8..af849895 100644 --- a/src/pcm/plugin/rate.c +++ b/src/pcm/plugin/rate.c @@ -48,6 +48,43 @@ struct rate_private_data { ssize_t old_src_size, old_dst_size; }; +static void mix_mono(struct rate_private_data *data, + signed short *src_ptr, int src_size, + signed short *dst_ptr, int dst_size) +{ + unsigned int pos; + signed int val; + signed int L_S1, L_S2; + + pos = data->pos; + L_S1 = data->last_L_S1; + L_S2 = data->last_L_S2; + if (pos >> SHIFT) { + src_ptr += ((pos >> SHIFT) - 1); pos &= MASK; + L_S1 = L_S2; + L_S2 = *src_ptr; + } + while (dst_size-- > 0) { + if (pos >> SHIFT) { + src_ptr += (pos >> SHIFT); pos &= MASK; + L_S1 = L_S2; + L_S2 = *src_ptr; + } + + val = L_S1 + ((L_S2 - L_S1) * (signed int)pos) / BITS; + if (val < -32768) + val = -32768; + else if (val > 32767) + val = 32767; + *dst_ptr++ = val; + + pos += data->pitch; + } + data->last_L_S1 = L_S1; + data->last_L_S2 = L_S2; + data->pos = pos; +} + static void mix_stereo(struct rate_private_data *data, signed short *src_ptr, int src_size, signed short *dst_ptr, int dst_size) @@ -115,7 +152,12 @@ static ssize_t rate_transfer(snd_pcm_plugin_t *plugin, data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin); if (data == NULL) return -EINVAL; - if (data->src_voices == 2) { + if (data->src_voices == 1) { + mix_mono(data, (signed short *)src_ptr, src_size / 2, + (signed short *)dst_ptr, dst_size / 2); + return (dst_size / 2) * 2; + } + else if (data->src_voices == 2) { mix_stereo(data, (signed short *)src_ptr, src_size / 4, (signed short *)dst_ptr, dst_size / 4); return (dst_size / 4) * 4; @@ -152,7 +194,9 @@ static ssize_t rate_src_size(snd_pcm_plugin_t *plugin, size_t size) if (!plugin || size <= 0) return -EINVAL; data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin); - res = (((size * data->pitch) + (BITS/2)) >> SHIFT) & ~3; + res = (((size * data->pitch) + (BITS/2)) >> SHIFT); + res = res / (data->src_voices*2) * (data->src_voices*2); + /* Why this? */ if (size < 128*1024) { if (data->old_src_size == size) return data->old_dst_size; @@ -170,7 +214,9 @@ static ssize_t rate_dst_size(snd_pcm_plugin_t *plugin, size_t size) if (!plugin || size <= 0) return -EINVAL; data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin); - res = (((size << SHIFT) + (data->pitch / 2)) / data->pitch) & ~3; + res = (((size << SHIFT) + (data->pitch / 2)) / data->pitch); + res = res / (data->src_voices*2) * (data->src_voices*2); + /* Why this? */ if (size < 128*1024) { if (data->old_dst_size == size) return data->old_src_size; @@ -180,8 +226,8 @@ static ssize_t rate_dst_size(snd_pcm_plugin_t *plugin, size_t size) return res; } -int snd_pcm_plugin_build_rate(int src_format, int src_rate, int src_voices, - int dst_format, int dst_rate, int dst_voices, +int snd_pcm_plugin_build_rate(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { struct rate_private_data *data; @@ -190,23 +236,33 @@ int snd_pcm_plugin_build_rate(int src_format, int src_rate, int src_voices, if (!r_plugin) return -EINVAL; *r_plugin = NULL; - if (src_voices != 2 || dst_voices != 2) + + if (src_format->interleave != dst_format->interleave) + return -EINVAL; + if (src_format->format != dst_format->format) return -EINVAL; - if (src_format != SND_PCM_SFMT_S16_LE || - dst_format != SND_PCM_SFMT_S16_LE) + if (!dst_format->interleave) + return -EINVAL; + if (src_format->voices != dst_format->voices) + return -EINVAL; + if (dst_format->voices != 1 && dst_format->voices != 2) + return -EINVAL; + + if (src_format->format != SND_PCM_SFMT_S16_LE || + dst_format->format != SND_PCM_SFMT_S16_LE) return -EINVAL; - if (src_rate == dst_rate) + if (src_format->rate == dst_format->rate) return -EINVAL; plugin = snd_pcm_plugin_build("rate format conversion", sizeof(struct rate_private_data)); if (plugin == NULL) return -ENOMEM; data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin); - data->src_voices = src_voices; - data->dst_voices = dst_voices; - data->src_rate = src_rate; - data->dst_rate = dst_rate; - data->pitch = ((src_rate << SHIFT) + (dst_rate >> 1)) / dst_rate; + data->src_voices = src_format->voices; + data->dst_voices = dst_format->voices; + data->src_rate = src_format->rate; + data->dst_rate = dst_format->rate; + data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate; data->pos = 0; data->last_L_S1 = data->last_R_S1 = 0; data->last_L_S2 = data->last_R_S2 = 0; diff --git a/src/pcm/plugin/voices.c b/src/pcm/plugin/voices.c index b5f3aedd..be7325b4 100644 --- a/src/pcm/plugin/voices.c +++ b/src/pcm/plugin/voices.c @@ -168,8 +168,8 @@ static ssize_t voices_dst_size(snd_pcm_plugin_t *plugin, size_t size) return (size * data->dst_voices) / data->src_voices; } -int snd_pcm_plugin_build_voices(int src_format, int src_voices, - int dst_format, int dst_voices, +int snd_pcm_plugin_build_voices(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { struct voices_private_data *data; @@ -178,15 +178,22 @@ int snd_pcm_plugin_build_voices(int src_format, int src_voices, if (!r_plugin) return -EINVAL; *r_plugin = NULL; - if (src_voices == dst_voices) + + if (src_format->interleave != dst_format->interleave) + return -EINVAL; + if (!dst_format->interleave) + return -EINVAL; + if (src_format->format != dst_format->format) + return -EINVAL; + if (src_format->rate != dst_format->rate) return -EINVAL; - if (src_voices < 1 || src_voices > 2 || - dst_voices < 1 || dst_voices > 2) + if (src_format->voices == dst_format->voices) return -EINVAL; - if (src_format != dst_format) + if (src_format->voices < 1 || src_format->voices > 2 || + dst_format->voices < 1 || dst_format->voices > 2) return -EINVAL; - if (src_format < SND_PCM_SFMT_S8 || src_format > SND_PCM_SFMT_U16_BE) { - if (src_format != SND_PCM_SFMT_MU_LAW && src_format != SND_PCM_SFMT_A_LAW) + if (src_format->format < SND_PCM_SFMT_S8 || src_format->format > SND_PCM_SFMT_U16_BE) { + if (src_format->format != SND_PCM_SFMT_MU_LAW && src_format->format != SND_PCM_SFMT_A_LAW) return -EINVAL; } plugin = snd_pcm_plugin_build("voices conversion", @@ -194,11 +201,11 @@ int snd_pcm_plugin_build_voices(int src_format, int src_voices, if (plugin == NULL) return -ENOMEM; data = (struct voices_private_data *)snd_pcm_plugin_extra_data(plugin); - data->src_voices = src_voices; - data->dst_voices = dst_voices; - data->width = snd_pcm_format_width(src_format); - data->flg_merge = src_voices > dst_voices; - data->flg_signed = snd_pcm_format_signed(src_format); + data->src_voices = src_format->voices; + data->dst_voices = dst_format->voices; + data->width = snd_pcm_format_width(src_format->format); + data->flg_merge = src_format->voices > dst_format->voices; + data->flg_signed = snd_pcm_format_signed(src_format->format); plugin->transfer = voices_transfer; plugin->src_size = voices_src_size; plugin->dst_size = voices_dst_size; diff --git a/src/pcm/plugin/volbal.c b/src/pcm/plugin/volbal.c new file mode 100644 index 00000000..3747e208 --- /dev/null +++ b/src/pcm/plugin/volbal.c @@ -0,0 +1,171 @@ +/* + * Volume and balance Plug-In + * Copyright (c) 1999 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../pcm_local.h" + + +#define VOLBAL_RESOLUTION 16 + +struct volbal_private_data { + int src_voices; + int noop; + int ttable[0]; +}; + +static void volbal(int voices, int samples, int *ttable, + signed short *src_ptr, signed short *dst_ptr) +{ + while (samples-- > 0) { + int dst_voice; + int *t = ttable; + for (dst_voice = 0; dst_voice < voices; ++dst_voice) { + int v = 0; + int src_voice; + signed short *s = src_ptr; + for (src_voice = 0; src_voice < voices; ++src_voice) { + v += *s++ * *t++ / VOLBAL_RESOLUTION; + } + *dst_ptr++ = v; + src_ptr += voices; + } + } +} + +static ssize_t volbal_transfer(snd_pcm_plugin_t *plugin, + char *src_ptr, size_t src_size, + char *dst_ptr, size_t dst_size) +{ + struct volbal_private_data *data; + if (plugin == NULL || src_ptr == NULL || src_size < 0 || + dst_ptr == NULL || dst_size < 0) + return -EINVAL; + if (src_size == 0) + return 0; + data = (struct volbal_private_data *)snd_pcm_plugin_extra_data(plugin); + if (data == NULL) + return -EINVAL; + if (data->noop) + return 0; + + volbal(data->src_voices, src_size / 2 / data->src_voices, data->ttable, + (signed short *)src_ptr, (signed short *)dst_ptr); + return src_size; +} + +static ssize_t volbal_src_size(snd_pcm_plugin_t *plugin, size_t size) +{ + if (!plugin || size <= 0) + return -EINVAL; + return size; +} + +static ssize_t volbal_dst_size(snd_pcm_plugin_t *plugin, size_t size) +{ + if (!plugin || size <= 0) + return -EINVAL; + return size; +} + + +static int volbal_load_ttable(struct volbal_private_data *data, + const int *src_ttable) +{ + int src_voice, dst_voice; + const int *sptr; + int *dptr; + data->noop = 1; + if (src_ttable == NULL) + return 0; + sptr = src_ttable; + dptr = data->ttable; + for (dst_voice = 0; dst_voice < data->src_voices; ++dst_voice) { + int t = 0; + for (src_voice = 0; src_voice < data->src_voices; ++src_voice) { + if (*sptr < 0 || *sptr > VOLBAL_RESOLUTION) + return -EINVAL; + if (src_voice == dst_voice) { + if (*sptr != VOLBAL_RESOLUTION) + data->noop = 0; + } + else { + if (*sptr != 0) + data->noop = 0; + } + t += *sptr; + *dptr++ = *sptr++; + } + if (t > VOLBAL_RESOLUTION) + return -EINVAL; + } + return 0; +} + + +int snd_pcm_plugin_build_volbal(snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + int *ttable, + snd_pcm_plugin_t **r_plugin) +{ + struct volbal_private_data *data; + snd_pcm_plugin_t *plugin; + int res; + + if (!r_plugin) + return -EINVAL; + *r_plugin = NULL; + if (src_format->interleave != dst_format->interleave) + return -EINVAL; + if (!dst_format->interleave) + return -EINVAL; + if (src_format->format != dst_format->format) + return -EINVAL; + if (src_format->rate != dst_format->rate) + return -EINVAL; + if (src_format->voices != dst_format->voices) + return -EINVAL; + + if (src_format->voices < 1) + return -EINVAL; + if (src_format->format != SND_PCM_SFMT_S16_LE) + return -EINVAL; + plugin = snd_pcm_plugin_build("Volume/balance conversion", + sizeof(struct volbal_private_data) + + sizeof(data->ttable[0]) * src_format->voices * src_format->voices); + if (plugin == NULL) + return -ENOMEM; + data = (struct volbal_private_data *)snd_pcm_plugin_extra_data(plugin); + data->src_voices = src_format->voices; + if ((res = volbal_load_ttable(data, ttable)) < 0) + return res; + + plugin->transfer = volbal_transfer; + plugin->src_size = volbal_src_size; + plugin->dst_size = volbal_dst_size; + *r_plugin = plugin; + return 0; +} -- 2.47.1