brintos

brintos / linux-shallow public Read only

0
0
Text · 21.8 KiB · 82e180c Raw
782 lines · c
1/*2 *  PCM Plug-In shared (kernel/library) code3 *  Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>4 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>5 *6 *7 *   This library is free software; you can redistribute it and/or modify8 *   it under the terms of the GNU Library General Public License as9 *   published by the Free Software Foundation; either version 2 of10 *   the License, or (at your option) any later version.11 *12 *   This program is distributed in the hope that it will be useful,13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the15 *   GNU Library General Public License for more details.16 *17 *   You should have received a copy of the GNU Library General Public18 *   License along with this library; if not, write to the Free Software19 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA20 *21 */22  23#if 024#define PLUGIN_DEBUG25#endif26 27#include <linux/slab.h>28#include <linux/time.h>29#include <linux/vmalloc.h>30#include <sound/core.h>31#include <sound/pcm.h>32#include <sound/pcm_params.h>33#include "pcm_plugin.h"34 35#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first)36#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last)37 38/*39 *  because some cards might have rates "very close", we ignore40 *  all "resampling" requests within +-5%41 */42static int rate_match(unsigned int src_rate, unsigned int dst_rate)43{44	unsigned int low = (src_rate * 95) / 100;45	unsigned int high = (src_rate * 105) / 100;46	return dst_rate >= low && dst_rate <= high;47}48 49static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t frames)50{51	struct snd_pcm_plugin_format *format;52	ssize_t width;53	size_t size;54	unsigned int channel;55	struct snd_pcm_plugin_channel *c;56 57	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK) {58		format = &plugin->src_format;59	} else {60		format = &plugin->dst_format;61	}62	width = snd_pcm_format_physical_width(format->format);63	if (width < 0)64		return width;65	size = array3_size(frames, format->channels, width);66	/* check for too large period size once again */67	if (size > 1024 * 1024)68		return -ENOMEM;69	if (snd_BUG_ON(size % 8))70		return -ENXIO;71	size /= 8;72	if (plugin->buf_frames < frames) {73		kvfree(plugin->buf);74		plugin->buf = kvzalloc(size, GFP_KERNEL);75		plugin->buf_frames = frames;76	}77	if (!plugin->buf) {78		plugin->buf_frames = 0;79		return -ENOMEM;80	}81	c = plugin->buf_channels;82	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {83		for (channel = 0; channel < format->channels; channel++, c++) {84			c->frames = frames;85			c->enabled = 1;86			c->wanted = 0;87			c->area.addr = plugin->buf;88			c->area.first = channel * width;89			c->area.step = format->channels * width;90		}91	} else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {92		if (snd_BUG_ON(size % format->channels))93			return -EINVAL;94		size /= format->channels;95		for (channel = 0; channel < format->channels; channel++, c++) {96			c->frames = frames;97			c->enabled = 1;98			c->wanted = 0;99			c->area.addr = plugin->buf + (channel * size);100			c->area.first = 0;101			c->area.step = width;102		}103	} else104		return -EINVAL;105	return 0;106}107 108int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)109{110	int err;111	if (snd_BUG_ON(!snd_pcm_plug_first(plug)))112		return -ENXIO;113	if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {114		struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug);115		while (plugin->next) {116			if (plugin->dst_frames)117				frames = plugin->dst_frames(plugin, frames);118			if ((snd_pcm_sframes_t)frames <= 0)119				return -ENXIO;120			plugin = plugin->next;121			err = snd_pcm_plugin_alloc(plugin, frames);122			if (err < 0)123				return err;124		}125	} else {126		struct snd_pcm_plugin *plugin = snd_pcm_plug_last(plug);127		while (plugin->prev) {128			if (plugin->src_frames)129				frames = plugin->src_frames(plugin, frames);130			if ((snd_pcm_sframes_t)frames <= 0)131				return -ENXIO;132			plugin = plugin->prev;133			err = snd_pcm_plugin_alloc(plugin, frames);134			if (err < 0)135				return err;136		}137	}138	return 0;139}140 141 142snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin,143				       snd_pcm_uframes_t frames,144				       struct snd_pcm_plugin_channel **channels)145{146	*channels = plugin->buf_channels;147	return frames;148}149 150int snd_pcm_plugin_build(struct snd_pcm_substream *plug,151			 const char *name,152			 struct snd_pcm_plugin_format *src_format,153			 struct snd_pcm_plugin_format *dst_format,154			 size_t extra,155			 struct snd_pcm_plugin **ret)156{157	struct snd_pcm_plugin *plugin;158	unsigned int channels;159	160	if (snd_BUG_ON(!plug))161		return -ENXIO;162	if (snd_BUG_ON(!src_format || !dst_format))163		return -ENXIO;164	plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL);165	if (plugin == NULL)166		return -ENOMEM;167	plugin->name = name;168	plugin->plug = plug;169	plugin->stream = snd_pcm_plug_stream(plug);170	plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;171	plugin->src_format = *src_format;172	plugin->src_width = snd_pcm_format_physical_width(src_format->format);173	snd_BUG_ON(plugin->src_width <= 0);174	plugin->dst_format = *dst_format;175	plugin->dst_width = snd_pcm_format_physical_width(dst_format->format);176	snd_BUG_ON(plugin->dst_width <= 0);177	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)178		channels = src_format->channels;179	else180		channels = dst_format->channels;181	plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL);182	if (plugin->buf_channels == NULL) {183		snd_pcm_plugin_free(plugin);184		return -ENOMEM;185	}186	plugin->client_channels = snd_pcm_plugin_client_channels;187	*ret = plugin;188	return 0;189}190 191int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin)192{193	if (! plugin)194		return 0;195	if (plugin->private_free)196		plugin->private_free(plugin);197	kfree(plugin->buf_channels);198	kvfree(plugin->buf);199	kfree(plugin);200	return 0;201}202 203static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug,204					 snd_pcm_sframes_t frames,205					 bool check_size)206{207	struct snd_pcm_plugin *plugin, *plugin_next;208 209	plugin = snd_pcm_plug_first(plug);210	while (plugin && frames > 0) {211		plugin_next = plugin->next;212		if (check_size && plugin->buf_frames &&213		    frames > plugin->buf_frames)214			frames = plugin->buf_frames;215		if (plugin->dst_frames) {216			frames = plugin->dst_frames(plugin, frames);217			if (frames < 0)218				return frames;219		}220		plugin = plugin_next;221	}222	return frames;223}224 225static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug,226					 snd_pcm_sframes_t frames,227					 bool check_size)228{229	struct snd_pcm_plugin *plugin, *plugin_prev;230 231	plugin = snd_pcm_plug_last(plug);232	while (plugin && frames > 0) {233		plugin_prev = plugin->prev;234		if (plugin->src_frames) {235			frames = plugin->src_frames(plugin, frames);236			if (frames < 0)237				return frames;238		}239		if (check_size && plugin->buf_frames &&240		    frames > plugin->buf_frames)241			frames = plugin->buf_frames;242		plugin = plugin_prev;243	}244	return frames;245}246 247snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)248{249	if (snd_BUG_ON(!plug))250		return -ENXIO;251	switch (snd_pcm_plug_stream(plug)) {252	case SNDRV_PCM_STREAM_PLAYBACK:253		return calc_src_frames(plug, drv_frames, false);254	case SNDRV_PCM_STREAM_CAPTURE:255		return calc_dst_frames(plug, drv_frames, false);256	default:257		snd_BUG();258		return -EINVAL;259	}260}261 262snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames)263{264	if (snd_BUG_ON(!plug))265		return -ENXIO;266	switch (snd_pcm_plug_stream(plug)) {267	case SNDRV_PCM_STREAM_PLAYBACK:268		return calc_dst_frames(plug, clt_frames, false);269	case SNDRV_PCM_STREAM_CAPTURE:270		return calc_src_frames(plug, clt_frames, false);271	default:272		snd_BUG();273		return -EINVAL;274	}275}276 277static int snd_pcm_plug_formats(const struct snd_mask *mask,278				snd_pcm_format_t format)279{280	struct snd_mask formats = *mask;281	u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |282		       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |283		       SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |284		       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |285		       SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |286		       SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE |287		       SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |288		       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |289		       SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);290	snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW);291	292	if (formats.bits[0] & lower_32_bits(linfmts))293		formats.bits[0] |= lower_32_bits(linfmts);294	if (formats.bits[1] & upper_32_bits(linfmts))295		formats.bits[1] |= upper_32_bits(linfmts);296	return snd_mask_test(&formats, (__force int)format);297}298 299static const snd_pcm_format_t preferred_formats[] = {300	SNDRV_PCM_FORMAT_S16_LE,301	SNDRV_PCM_FORMAT_S16_BE,302	SNDRV_PCM_FORMAT_U16_LE,303	SNDRV_PCM_FORMAT_U16_BE,304	SNDRV_PCM_FORMAT_S24_3LE,305	SNDRV_PCM_FORMAT_S24_3BE,306	SNDRV_PCM_FORMAT_U24_3LE,307	SNDRV_PCM_FORMAT_U24_3BE,308	SNDRV_PCM_FORMAT_S24_LE,309	SNDRV_PCM_FORMAT_S24_BE,310	SNDRV_PCM_FORMAT_U24_LE,311	SNDRV_PCM_FORMAT_U24_BE,312	SNDRV_PCM_FORMAT_S32_LE,313	SNDRV_PCM_FORMAT_S32_BE,314	SNDRV_PCM_FORMAT_U32_LE,315	SNDRV_PCM_FORMAT_U32_BE,316	SNDRV_PCM_FORMAT_S8,317	SNDRV_PCM_FORMAT_U8318};319 320snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,321					   const struct snd_mask *format_mask)322{323	int i;324 325	if (snd_mask_test(format_mask, (__force int)format))326		return format;327	if (!snd_pcm_plug_formats(format_mask, format))328		return (__force snd_pcm_format_t)-EINVAL;329	if (snd_pcm_format_linear(format)) {330		unsigned int width = snd_pcm_format_width(format);331		int unsignd = snd_pcm_format_unsigned(format) > 0;332		int big = snd_pcm_format_big_endian(format) > 0;333		unsigned int badness, best = -1;334		snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1;335		for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) {336			snd_pcm_format_t f = preferred_formats[i];337			unsigned int w;338			if (!snd_mask_test(format_mask, (__force int)f))339				continue;340			w = snd_pcm_format_width(f);341			if (w >= width)342				badness = w - width;343			else344				badness = width - w + 32;345			badness += snd_pcm_format_unsigned(f) != unsignd;346			badness += snd_pcm_format_big_endian(f) != big;347			if (badness < best) {348				best_format = f;349				best = badness;350			}351		}352		if ((__force int)best_format >= 0)353			return best_format;354		else355			return (__force snd_pcm_format_t)-EINVAL;356	} else {357		switch (format) {358		case SNDRV_PCM_FORMAT_MU_LAW:359			for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {360				snd_pcm_format_t format1 = preferred_formats[i];361				if (snd_mask_test(format_mask, (__force int)format1))362					return format1;363			}364			fallthrough;365		default:366			return (__force snd_pcm_format_t)-EINVAL;367		}368	}369}370 371int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug,372				struct snd_pcm_hw_params *params,373				struct snd_pcm_hw_params *slave_params)374{375	struct snd_pcm_plugin_format tmpformat;376	struct snd_pcm_plugin_format dstformat;377	struct snd_pcm_plugin_format srcformat;378	snd_pcm_access_t src_access, dst_access;379	struct snd_pcm_plugin *plugin = NULL;380	int err;381	int stream = snd_pcm_plug_stream(plug);382	int slave_interleaved = (params_channels(slave_params) == 1 ||383				 params_access(slave_params) == SNDRV_PCM_ACCESS_RW_INTERLEAVED);384 385	switch (stream) {386	case SNDRV_PCM_STREAM_PLAYBACK:387		dstformat.format = params_format(slave_params);388		dstformat.rate = params_rate(slave_params);389		dstformat.channels = params_channels(slave_params);390		srcformat.format = params_format(params);391		srcformat.rate = params_rate(params);392		srcformat.channels = params_channels(params);393		src_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;394		dst_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :395						  SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);396		break;397	case SNDRV_PCM_STREAM_CAPTURE:398		dstformat.format = params_format(params);399		dstformat.rate = params_rate(params);400		dstformat.channels = params_channels(params);401		srcformat.format = params_format(slave_params);402		srcformat.rate = params_rate(slave_params);403		srcformat.channels = params_channels(slave_params);404		src_access = (slave_interleaved ? SNDRV_PCM_ACCESS_RW_INTERLEAVED :405						  SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);406		dst_access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;407		break;408	default:409		snd_BUG();410		return -EINVAL;411	}412	tmpformat = srcformat;413		414	pdprintf("srcformat: format=%i, rate=%i, channels=%i\n", 415		 srcformat.format,416		 srcformat.rate,417		 srcformat.channels);418	pdprintf("dstformat: format=%i, rate=%i, channels=%i\n", 419		 dstformat.format,420		 dstformat.rate,421		 dstformat.channels);422 423	/* Format change (linearization) */424	if (! rate_match(srcformat.rate, dstformat.rate) &&425	    ! snd_pcm_format_linear(srcformat.format)) {426		if (srcformat.format != SNDRV_PCM_FORMAT_MU_LAW)427			return -EINVAL;428		tmpformat.format = SNDRV_PCM_FORMAT_S16;429		err = snd_pcm_plugin_build_mulaw(plug,430						 &srcformat, &tmpformat,431						 &plugin);432		if (err < 0)433			return err;434		err = snd_pcm_plugin_append(plugin);435		if (err < 0) {436			snd_pcm_plugin_free(plugin);437			return err;438		}439		srcformat = tmpformat;440		src_access = dst_access;441	}442 443	/* channels reduction */444	if (srcformat.channels > dstformat.channels) {445		tmpformat.channels = dstformat.channels;446		err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin);447		pdprintf("channels reduction: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);448		if (err < 0)449			return err;450		err = snd_pcm_plugin_append(plugin);451		if (err < 0) {452			snd_pcm_plugin_free(plugin);453			return err;454		}455		srcformat = tmpformat;456		src_access = dst_access;457	}458 459	/* rate resampling */460	if (!rate_match(srcformat.rate, dstformat.rate)) {461		if (srcformat.format != SNDRV_PCM_FORMAT_S16) {462			/* convert to S16 for resampling */463			tmpformat.format = SNDRV_PCM_FORMAT_S16;464			err = snd_pcm_plugin_build_linear(plug,465							  &srcformat, &tmpformat,466							  &plugin);467			if (err < 0)468				return err;469			err = snd_pcm_plugin_append(plugin);470			if (err < 0) {471				snd_pcm_plugin_free(plugin);472				return err;473			}474			srcformat = tmpformat;475			src_access = dst_access;476		}477		tmpformat.rate = dstformat.rate;478        	err = snd_pcm_plugin_build_rate(plug,479        					&srcformat, &tmpformat,480						&plugin);481		pdprintf("rate down resampling: src=%i, dst=%i returns %i\n", srcformat.rate, tmpformat.rate, err);482		if (err < 0)483			return err;484		err = snd_pcm_plugin_append(plugin);485		if (err < 0) {486			snd_pcm_plugin_free(plugin);487			return err;488		}489		srcformat = tmpformat;490		src_access = dst_access;491        }492 493	/* format change */494	if (srcformat.format != dstformat.format) {495		tmpformat.format = dstformat.format;496		if (srcformat.format == SNDRV_PCM_FORMAT_MU_LAW ||497		    tmpformat.format == SNDRV_PCM_FORMAT_MU_LAW) {498			err = snd_pcm_plugin_build_mulaw(plug,499							 &srcformat, &tmpformat,500							 &plugin);501		}502		else if (snd_pcm_format_linear(srcformat.format) &&503			 snd_pcm_format_linear(tmpformat.format)) {504			err = snd_pcm_plugin_build_linear(plug,505							  &srcformat, &tmpformat,506							  &plugin);507		}508		else509			return -EINVAL;510		pdprintf("format change: src=%i, dst=%i returns %i\n", srcformat.format, tmpformat.format, err);511		if (err < 0)512			return err;513		err = snd_pcm_plugin_append(plugin);514		if (err < 0) {515			snd_pcm_plugin_free(plugin);516			return err;517		}518		srcformat = tmpformat;519		src_access = dst_access;520	}521 522	/* channels extension */523	if (srcformat.channels < dstformat.channels) {524		tmpformat.channels = dstformat.channels;525		err = snd_pcm_plugin_build_route(plug, &srcformat, &tmpformat, &plugin);526		pdprintf("channels extension: src=%i, dst=%i returns %i\n", srcformat.channels, tmpformat.channels, err);527		if (err < 0)528			return err;529		err = snd_pcm_plugin_append(plugin);530		if (err < 0) {531			snd_pcm_plugin_free(plugin);532			return err;533		}534		srcformat = tmpformat;535		src_access = dst_access;536	}537 538	/* de-interleave */539	if (src_access != dst_access) {540		err = snd_pcm_plugin_build_copy(plug,541						&srcformat,542						&tmpformat,543						&plugin);544		pdprintf("interleave change (copy: returns %i)\n", err);545		if (err < 0)546			return err;547		err = snd_pcm_plugin_append(plugin);548		if (err < 0) {549			snd_pcm_plugin_free(plugin);550			return err;551		}552	}553 554	return 0;555}556 557snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plug,558					 char *buf,559					 snd_pcm_uframes_t count,560					 struct snd_pcm_plugin_channel **channels)561{562	struct snd_pcm_plugin *plugin;563	struct snd_pcm_plugin_channel *v;564	struct snd_pcm_plugin_format *format;565	int width, nchannels, channel;566	int stream = snd_pcm_plug_stream(plug);567 568	if (snd_BUG_ON(!buf))569		return -ENXIO;570	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {571		plugin = snd_pcm_plug_first(plug);572		format = &plugin->src_format;573	} else {574		plugin = snd_pcm_plug_last(plug);575		format = &plugin->dst_format;576	}577	v = plugin->buf_channels;578	*channels = v;579	width = snd_pcm_format_physical_width(format->format);580	if (width < 0)581		return width;582	nchannels = format->channels;583	if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&584		       format->channels > 1))585		return -ENXIO;586	for (channel = 0; channel < nchannels; channel++, v++) {587		v->frames = count;588		v->enabled = 1;589		v->wanted = (stream == SNDRV_PCM_STREAM_CAPTURE);590		v->area.addr = buf;591		v->area.first = channel * width;592		v->area.step = nchannels * width;593	}594	return count;595}596 597snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *src_channels, snd_pcm_uframes_t size)598{599	struct snd_pcm_plugin *plugin, *next;600	struct snd_pcm_plugin_channel *dst_channels;601	int err;602	snd_pcm_sframes_t frames = size;603 604	plugin = snd_pcm_plug_first(plug);605	while (plugin) {606		if (frames <= 0)607			return frames;608		next = plugin->next;609		if (next) {610			snd_pcm_sframes_t frames1 = frames;611			if (plugin->dst_frames) {612				frames1 = plugin->dst_frames(plugin, frames);613				if (frames1 <= 0)614					return frames1;615			}616			err = next->client_channels(next, frames1, &dst_channels);617			if (err < 0)618				return err;619			if (err != frames1) {620				frames = err;621				if (plugin->src_frames) {622					frames = plugin->src_frames(plugin, frames1);623					if (frames <= 0)624						return frames;625				}626			}627		} else628			dst_channels = NULL;629		pdprintf("write plugin: %s, %li\n", plugin->name, frames);630		frames = plugin->transfer(plugin, src_channels, dst_channels, frames);631		if (frames < 0)632			return frames;633		src_channels = dst_channels;634		plugin = next;635	}636	return calc_src_frames(plug, frames, true);637}638 639snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *dst_channels_final, snd_pcm_uframes_t size)640{641	struct snd_pcm_plugin *plugin, *next;642	struct snd_pcm_plugin_channel *src_channels, *dst_channels;643	snd_pcm_sframes_t frames = size;644	int err;645 646	frames = calc_src_frames(plug, frames, true);647	if (frames < 0)648		return frames;649 650	src_channels = NULL;651	plugin = snd_pcm_plug_first(plug);652	while (plugin && frames > 0) {653		next = plugin->next;654		if (next) {655			err = plugin->client_channels(plugin, frames, &dst_channels);656			if (err < 0)657				return err;658			frames = err;659		} else {660			dst_channels = dst_channels_final;661		}662		pdprintf("read plugin: %s, %li\n", plugin->name, frames);663		frames = plugin->transfer(plugin, src_channels, dst_channels, frames);664		if (frames < 0)665			return frames;666		plugin = next;667		src_channels = dst_channels;668	}669	return frames;670}671 672int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset,673			 size_t samples, snd_pcm_format_t format)674{675	/* FIXME: sub byte resolution and odd dst_offset */676	unsigned char *dst;677	unsigned int dst_step;678	int width;679	const unsigned char *silence;680	if (!dst_area->addr)681		return 0;682	dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;683	width = snd_pcm_format_physical_width(format);684	if (width <= 0)685		return -EINVAL;686	if (dst_area->step == (unsigned int) width && width >= 8)687		return snd_pcm_format_set_silence(format, dst, samples);688	silence = snd_pcm_format_silence_64(format);689	if (! silence)690		return -EINVAL;691	dst_step = dst_area->step / 8;692	if (width == 4) {693		/* Ima ADPCM */694		int dstbit = dst_area->first % 8;695		int dstbit_step = dst_area->step % 8;696		while (samples-- > 0) {697			if (dstbit)698				*dst &= 0xf0;699			else700				*dst &= 0x0f;701			dst += dst_step;702			dstbit += dstbit_step;703			if (dstbit == 8) {704				dst++;705				dstbit = 0;706			}707		}708	} else {709		width /= 8;710		while (samples-- > 0) {711			memcpy(dst, silence, width);712			dst += dst_step;713		}714	}715	return 0;716}717 718int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset,719		      const struct snd_pcm_channel_area *dst_area, size_t dst_offset,720		      size_t samples, snd_pcm_format_t format)721{722	/* FIXME: sub byte resolution and odd dst_offset */723	char *src, *dst;724	int width;725	int src_step, dst_step;726	src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8;727	if (!src_area->addr)728		return snd_pcm_area_silence(dst_area, dst_offset, samples, format);729	dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8;730	if (!dst_area->addr)731		return 0;732	width = snd_pcm_format_physical_width(format);733	if (width <= 0)734		return -EINVAL;735	if (src_area->step == (unsigned int) width &&736	    dst_area->step == (unsigned int) width && width >= 8) {737		size_t bytes = samples * width / 8;738		memcpy(dst, src, bytes);739		return 0;740	}741	src_step = src_area->step / 8;742	dst_step = dst_area->step / 8;743	if (width == 4) {744		/* Ima ADPCM */745		int srcbit = src_area->first % 8;746		int srcbit_step = src_area->step % 8;747		int dstbit = dst_area->first % 8;748		int dstbit_step = dst_area->step % 8;749		while (samples-- > 0) {750			unsigned char srcval;751			if (srcbit)752				srcval = *src & 0x0f;753			else754				srcval = (*src & 0xf0) >> 4;755			if (dstbit)756				*dst = (*dst & 0xf0) | srcval;757			else758				*dst = (*dst & 0x0f) | (srcval << 4);759			src += src_step;760			srcbit += srcbit_step;761			if (srcbit == 8) {762				src++;763				srcbit = 0;764			}765			dst += dst_step;766			dstbit += dstbit_step;767			if (dstbit == 8) {768				dst++;769				dstbit = 0;770			}771		}772	} else {773		width /= 8;774		while (samples-- > 0) {775			memcpy(dst, src, width);776			src += src_step;777			dst += dst_step;778		}779	}780	return 0;781}782