Blame src/pcm/pcm_adpcm.c

Packit 4a16fb
/**
Packit 4a16fb
 * \file pcm/pcm_adpcm.c
Packit 4a16fb
 * \ingroup PCM_Plugins
Packit 4a16fb
 * \brief PCM Ima-ADPCM Conversion Plugin Interface
Packit 4a16fb
 * \author Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 * \author Uros Bizjak <uros@kss-loka.si>
Packit 4a16fb
 * \author Jaroslav Kysela <perex@perex.cz>
Packit 4a16fb
 * \date 2000-2001
Packit 4a16fb
 */
Packit 4a16fb
/*
Packit 4a16fb
 *  PCM - Ima-ADPCM conversion
Packit 4a16fb
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 *  Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
Packit 4a16fb
 *                        Jaroslav Kysela <perex@perex.cz>
Packit 4a16fb
 *
Packit 4a16fb
 *  Based on Version 1.2, 18-Dec-92 implementation of Intel/DVI ADPCM code
Packit 4a16fb
 *  by Jack Jansen, CWI, Amsterdam <Jack.Jansen@cwi.nl>, Copyright 1992
Packit 4a16fb
 *  by Stichting Mathematisch Centrum, Amsterdam, The Netherlands.
Packit 4a16fb
 *
Packit 4a16fb
 *   This library is free software; you can redistribute it and/or modify
Packit 4a16fb
 *   it under the terms of the GNU Lesser General Public License as
Packit 4a16fb
 *   published by the Free Software Foundation; either version 2.1 of
Packit 4a16fb
 *   the License, or (at your option) any later version.
Packit 4a16fb
 *
Packit 4a16fb
 *   This program is distributed in the hope that it will be useful,
Packit 4a16fb
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4a16fb
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4a16fb
 *   GNU Lesser General Public License for more details.
Packit 4a16fb
 *
Packit 4a16fb
 *   You should have received a copy of the GNU Lesser General Public
Packit 4a16fb
 *   License along with this library; if not, write to the Free Software
Packit 4a16fb
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 4a16fb
 *
Packit 4a16fb
 */
Packit 4a16fb
  
Packit 4a16fb
/*
Packit 4a16fb
These routines convert 16 bit linear PCM samples to 4 bit ADPCM code
Packit 4a16fb
and vice versa. The ADPCM code used is the Intel/DVI ADPCM code which
Packit 4a16fb
is being recommended by the IMA Digital Audio Technical Working Group.
Packit 4a16fb
Packit 4a16fb
The algorithm for this coder was taken from:
Packit 4a16fb
Proposal for Standardized Audio Interstreamge Formats,
Packit 4a16fb
IMA compatibility project proceedings, Vol 2, Issue 2, May 1992.
Packit 4a16fb
Packit 4a16fb
- No, this is *not* a G.721 coder/decoder. The algorithm used by G.721
Packit 4a16fb
  is very complicated, requiring oodles of floating-point ops per
Packit 4a16fb
  sample (resulting in very poor performance). I have not done any
Packit 4a16fb
  tests myself but various people have assured my that 721 quality is
Packit 4a16fb
  actually lower than DVI quality.
Packit 4a16fb
Packit 4a16fb
- No, it probably isn't a RIFF ADPCM decoder either. Trying to decode
Packit 4a16fb
  RIFF ADPCM with these routines seems to result in something
Packit 4a16fb
  recognizable but very distorted.
Packit 4a16fb
Packit 4a16fb
- No, it is not a CDROM-XA coder either, as far as I know. I haven't
Packit 4a16fb
  come across a good description of XA yet.
Packit 4a16fb
 */
Packit 4a16fb
Packit 4a16fb
#include "bswap.h"
Packit 4a16fb
#include "pcm_local.h"
Packit 4a16fb
#include "pcm_plugin.h"
Packit 4a16fb
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
Packit 4a16fb
#ifndef PIC
Packit 4a16fb
/* entry for static linking */
Packit 4a16fb
const char *_snd_module_pcm_adpcm = "";
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
Packit 4a16fb
typedef void (*adpcm_f)(const snd_pcm_channel_area_t *dst_areas,
Packit 4a16fb
			snd_pcm_uframes_t dst_offset,
Packit 4a16fb
			const snd_pcm_channel_area_t *src_areas,
Packit 4a16fb
			snd_pcm_uframes_t src_offset,
Packit 4a16fb
			unsigned int channels, snd_pcm_uframes_t frames,
Packit 4a16fb
			unsigned int getputidx,
Packit 4a16fb
			snd_pcm_adpcm_state_t *states);
Packit 4a16fb
Packit 4a16fb
typedef struct {
Packit 4a16fb
	/* This field need to be the first */
Packit 4a16fb
	snd_pcm_plugin_t plug;
Packit 4a16fb
	unsigned int getput_idx;
Packit 4a16fb
	adpcm_f func;
Packit 4a16fb
	snd_pcm_format_t sformat;
Packit 4a16fb
	snd_pcm_adpcm_state_t *states;
Packit 4a16fb
} snd_pcm_adpcm_t;
Packit 4a16fb
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
/* First table lookup for Ima-ADPCM quantizer */
Packit 4a16fb
static const char IndexAdjust[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
Packit 4a16fb
Packit 4a16fb
/* Second table lookup for Ima-ADPCM quantizer */
Packit 4a16fb
static const short StepSize[89] = {
Packit 4a16fb
	7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
Packit 4a16fb
	19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
Packit 4a16fb
	50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
Packit 4a16fb
	130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
Packit 4a16fb
	337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
Packit 4a16fb
	876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
Packit 4a16fb
	2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
Packit 4a16fb
	5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
Packit 4a16fb
	15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
static char adpcm_encoder(int sl, snd_pcm_adpcm_state_t * state)
Packit 4a16fb
{
Packit 4a16fb
	short diff;		/* Difference between sl and predicted sample */
Packit 4a16fb
	short pred_diff;	/* Predicted difference to next sample */
Packit 4a16fb
Packit 4a16fb
	unsigned char sign;	/* sign of diff */
Packit 4a16fb
	short step;		/* holds previous StepSize value */
Packit 4a16fb
	unsigned char adjust_idx;	/* Index to IndexAdjust lookup table */
Packit 4a16fb
Packit 4a16fb
	int i;
Packit 4a16fb
Packit 4a16fb
	/* Compute difference to previous predicted value */
Packit 4a16fb
	diff = sl - state->pred_val;
Packit 4a16fb
	sign = (diff < 0) ? 0x8 : 0x0;
Packit 4a16fb
	if (sign) {
Packit 4a16fb
		diff = -diff;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/*
Packit 4a16fb
	 * This code *approximately* computes:
Packit 4a16fb
	 *    adjust_idx = diff * 4 / step;
Packit 4a16fb
	 *    pred_diff = (adjust_idx + 0.5) * step / 4;
Packit 4a16fb
	 *
Packit 4a16fb
	 * But in shift step bits are dropped. The net result of this is
Packit 4a16fb
	 * that even if you have fast mul/div hardware you cannot put it to
Packit 4a16fb
	 * good use since the fix-up would be too expensive.
Packit 4a16fb
	 */
Packit 4a16fb
Packit 4a16fb
	step = StepSize[state->step_idx];
Packit 4a16fb
Packit 4a16fb
	/* Divide and clamp */
Packit 4a16fb
	pred_diff = step >> 3;
Packit 4a16fb
	for (adjust_idx = 0, i = 0x4; i; i >>= 1, step >>= 1) {
Packit 4a16fb
		if (diff >= step) {
Packit 4a16fb
			adjust_idx |= i;
Packit 4a16fb
			diff -= step;
Packit 4a16fb
			pred_diff += step;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* Update and clamp previous predicted value */
Packit 4a16fb
	state->pred_val += sign ? -pred_diff : pred_diff;
Packit 4a16fb
Packit 4a16fb
	if (state->pred_val > 32767) {
Packit 4a16fb
		state->pred_val = 32767;
Packit 4a16fb
	} else if (state->pred_val < -32768) {
Packit 4a16fb
		state->pred_val = -32768;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* Update and clamp StepSize lookup table index */
Packit 4a16fb
	state->step_idx += IndexAdjust[adjust_idx];
Packit 4a16fb
Packit 4a16fb
	if (state->step_idx < 0) {
Packit 4a16fb
		state->step_idx = 0;
Packit 4a16fb
	} else if (state->step_idx > 88) {
Packit 4a16fb
		state->step_idx = 88;
Packit 4a16fb
	}
Packit 4a16fb
	return (sign | adjust_idx);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
Packit 4a16fb
static int adpcm_decoder(unsigned char code, snd_pcm_adpcm_state_t * state)
Packit 4a16fb
{
Packit 4a16fb
	short pred_diff;	/* Predicted difference to next sample */
Packit 4a16fb
	short step;		/* holds previous StepSize value */
Packit 4a16fb
	char sign;
Packit 4a16fb
Packit 4a16fb
	int i;
Packit 4a16fb
Packit 4a16fb
	/* Separate sign and magnitude */
Packit 4a16fb
	sign = code & 0x8;
Packit 4a16fb
	code &= 0x7;
Packit 4a16fb
Packit 4a16fb
	/*
Packit 4a16fb
	 * Computes pred_diff = (code + 0.5) * step / 4,
Packit 4a16fb
	 * but see comment in adpcm_coder.
Packit 4a16fb
	 */
Packit 4a16fb
Packit 4a16fb
	step = StepSize[state->step_idx];
Packit 4a16fb
Packit 4a16fb
	/* Compute difference and new predicted value */
Packit 4a16fb
	pred_diff = step >> 3;
Packit 4a16fb
	for (i = 0x4; i; i >>= 1, step >>= 1) {
Packit 4a16fb
		if (code & i) {
Packit 4a16fb
			pred_diff += step;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	state->pred_val += (sign) ? -pred_diff : pred_diff;
Packit 4a16fb
Packit 4a16fb
	/* Clamp output value */
Packit 4a16fb
	if (state->pred_val > 32767) {
Packit 4a16fb
		state->pred_val = 32767;
Packit 4a16fb
	} else if (state->pred_val < -32768) {
Packit 4a16fb
		state->pred_val = -32768;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* Find new StepSize index value */
Packit 4a16fb
	state->step_idx += IndexAdjust[code];
Packit 4a16fb
Packit 4a16fb
	if (state->step_idx < 0) {
Packit 4a16fb
		state->step_idx = 0;
Packit 4a16fb
	} else if (state->step_idx > 88) {
Packit 4a16fb
		state->step_idx = 88;
Packit 4a16fb
	}
Packit 4a16fb
	return (state->pred_val);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
Packit 4a16fb
void snd_pcm_adpcm_decode(const snd_pcm_channel_area_t *dst_areas,
Packit 4a16fb
			  snd_pcm_uframes_t dst_offset,
Packit 4a16fb
			  const snd_pcm_channel_area_t *src_areas,
Packit 4a16fb
			  snd_pcm_uframes_t src_offset,
Packit 4a16fb
			  unsigned int channels, snd_pcm_uframes_t frames,
Packit 4a16fb
			  unsigned int putidx,
Packit 4a16fb
			  snd_pcm_adpcm_state_t *states)
Packit 4a16fb
{
Packit 4a16fb
#define PUT16_LABELS
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef PUT16_LABELS
Packit 4a16fb
	void *put = put16_labels[putidx];
Packit 4a16fb
	unsigned int channel;
Packit 4a16fb
	for (channel = 0; channel < channels; ++channel, ++states) {
Packit 4a16fb
		const char *src;
Packit 4a16fb
		int srcbit;
Packit 4a16fb
		char *dst;
Packit 4a16fb
		int src_step, srcbit_step, dst_step;
Packit 4a16fb
		snd_pcm_uframes_t frames1;
Packit 4a16fb
		const snd_pcm_channel_area_t *src_area = &src_areas[channel];
Packit 4a16fb
		const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
Packit 4a16fb
		srcbit = src_area->first + src_area->step * src_offset;
Packit 4a16fb
		src = (const char *) src_area->addr + srcbit / 8;
Packit 4a16fb
		srcbit %= 8;
Packit 4a16fb
		src_step = src_area->step / 8;
Packit 4a16fb
		srcbit_step = src_area->step % 8;
Packit 4a16fb
		dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
Packit 4a16fb
		dst_step = snd_pcm_channel_area_step(dst_area);
Packit 4a16fb
		frames1 = frames;
Packit 4a16fb
		while (frames1-- > 0) {
Packit 4a16fb
			int16_t sample;
Packit 4a16fb
			unsigned char v;
Packit 4a16fb
			if (srcbit)
Packit 4a16fb
				v = *src & 0x0f;
Packit 4a16fb
			else
Packit 4a16fb
				v = (*src >> 4) & 0x0f;
Packit 4a16fb
			sample = adpcm_decoder(v, states);
Packit 4a16fb
			goto *put;
Packit 4a16fb
#define PUT16_END after
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef PUT16_END
Packit 4a16fb
		after:
Packit 4a16fb
			src += src_step;
Packit 4a16fb
			srcbit += srcbit_step;
Packit 4a16fb
			if (srcbit == 8) {
Packit 4a16fb
				src++;
Packit 4a16fb
				srcbit = 0;
Packit 4a16fb
			}
Packit 4a16fb
			dst += dst_step;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
void snd_pcm_adpcm_encode(const snd_pcm_channel_area_t *dst_areas,
Packit 4a16fb
			  snd_pcm_uframes_t dst_offset,
Packit 4a16fb
			  const snd_pcm_channel_area_t *src_areas,
Packit 4a16fb
			  snd_pcm_uframes_t src_offset,
Packit 4a16fb
			  unsigned int channels, snd_pcm_uframes_t frames,
Packit 4a16fb
			  unsigned int getidx,
Packit 4a16fb
			  snd_pcm_adpcm_state_t *states)
Packit 4a16fb
{
Packit 4a16fb
#define GET16_LABELS
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef GET16_LABELS
Packit 4a16fb
	void *get = get16_labels[getidx];
Packit 4a16fb
	unsigned int channel;
Packit 4a16fb
	int16_t sample = 0;
Packit 4a16fb
	for (channel = 0; channel < channels; ++channel, ++states) {
Packit 4a16fb
		const char *src;
Packit 4a16fb
		char *dst;
Packit 4a16fb
		int dstbit;
Packit 4a16fb
		int src_step, dst_step, dstbit_step;
Packit 4a16fb
		snd_pcm_uframes_t frames1;
Packit 4a16fb
		const snd_pcm_channel_area_t *src_area = &src_areas[channel];
Packit 4a16fb
		const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
Packit 4a16fb
		src = snd_pcm_channel_area_addr(src_area, src_offset);
Packit 4a16fb
		src_step = snd_pcm_channel_area_step(src_area);
Packit 4a16fb
		dstbit = dst_area->first + dst_area->step * dst_offset;
Packit 4a16fb
		dst = (char *) dst_area->addr + dstbit / 8;
Packit 4a16fb
		dstbit %= 8;
Packit 4a16fb
		dst_step = dst_area->step / 8;
Packit 4a16fb
		dstbit_step = dst_area->step % 8;
Packit 4a16fb
		frames1 = frames;
Packit 4a16fb
		while (frames1-- > 0) {
Packit 4a16fb
			int v;
Packit 4a16fb
			goto *get;
Packit 4a16fb
#define GET16_END after
Packit 4a16fb
#include "plugin_ops.h"
Packit 4a16fb
#undef GET16_END
Packit 4a16fb
		after:
Packit 4a16fb
			v = adpcm_encoder(sample, states);
Packit 4a16fb
			if (dstbit)
Packit 4a16fb
				*dst = (*dst & 0xf0) | v;
Packit 4a16fb
			else
Packit 4a16fb
				*dst = (*dst & 0x0f) | (v << 4);
Packit 4a16fb
			src += src_step;
Packit 4a16fb
			dst += dst_step;
Packit 4a16fb
			dstbit += dstbit_step;
Packit 4a16fb
			if (dstbit == 8) {
Packit 4a16fb
				dst++;
Packit 4a16fb
				dstbit = 0;
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_adpcm_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_adpcm_t *adpcm = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
Packit 4a16fb
	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
Packit 4a16fb
					 &access_mask);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
Packit 4a16fb
		snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
Packit 4a16fb
		err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
Packit 4a16fb
						 &format_mask);
Packit 4a16fb
	} else {
Packit 4a16fb
		err = _snd_pcm_hw_params_set_format(params,
Packit 4a16fb
						   SND_PCM_FORMAT_IMA_ADPCM);
Packit 4a16fb
	}
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	err = _snd_pcm_hw_params_set_subformat(params,
Packit 4a16fb
					       SND_PCM_SUBFORMAT_STD);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_adpcm_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_adpcm_t *adpcm = pcm->private_data;
Packit 4a16fb
	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
Packit 4a16fb
	_snd_pcm_hw_params_any(sparams);
Packit 4a16fb
	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
Packit 4a16fb
				   &saccess_mask);
Packit 4a16fb
	_snd_pcm_hw_params_set_format(sparams, adpcm->sformat);
Packit 4a16fb
	_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_adpcm_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
Packit 4a16fb
					    snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_RATE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIODS |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit 4a16fb
	err = _snd_pcm_hw_params_refine(sparams, links, params);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
	
Packit 4a16fb
static int snd_pcm_adpcm_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
Packit 4a16fb
					    snd_pcm_hw_params_t *sparams)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_RATE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIODS |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_PERIOD_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_BUFFER_TIME |
Packit 4a16fb
			      SND_PCM_HW_PARBIT_TICK_TIME);
Packit 4a16fb
	err = _snd_pcm_hw_params_refine(params, links, sparams);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_adpcm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	return snd_pcm_hw_refine_slave(pcm, params,
Packit 4a16fb
				       snd_pcm_adpcm_hw_refine_cprepare,
Packit 4a16fb
				       snd_pcm_adpcm_hw_refine_cchange,
Packit 4a16fb
				       snd_pcm_adpcm_hw_refine_sprepare,
Packit 4a16fb
				       snd_pcm_adpcm_hw_refine_schange,
Packit 4a16fb
				       snd_pcm_generic_hw_refine);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_adpcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_adpcm_t *adpcm = pcm->private_data;
Packit 4a16fb
	snd_pcm_format_t format;
Packit 4a16fb
	int err = snd_pcm_hw_params_slave(pcm, params,
Packit 4a16fb
					  snd_pcm_adpcm_hw_refine_cchange,
Packit 4a16fb
					  snd_pcm_adpcm_hw_refine_sprepare,
Packit 4a16fb
					  snd_pcm_adpcm_hw_refine_schange,
Packit 4a16fb
					  snd_pcm_generic_hw_params);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
Packit 4a16fb
	err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
Packit 4a16fb
	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
Packit 4a16fb
		if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
Packit 4a16fb
			adpcm->getput_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S16);
Packit 4a16fb
			adpcm->func = snd_pcm_adpcm_encode;
Packit 4a16fb
		} else {
Packit 4a16fb
			adpcm->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, adpcm->sformat);
Packit 4a16fb
			adpcm->func = snd_pcm_adpcm_decode;
Packit 4a16fb
		}
Packit 4a16fb
	} else {
Packit 4a16fb
		if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
Packit 4a16fb
			adpcm->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, format);
Packit 4a16fb
			adpcm->func = snd_pcm_adpcm_decode;
Packit 4a16fb
		} else {
Packit 4a16fb
			adpcm->getput_idx = snd_pcm_linear_get_index(adpcm->sformat, SND_PCM_FORMAT_S16);
Packit 4a16fb
			adpcm->func = snd_pcm_adpcm_encode;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	assert(!adpcm->states);
Packit 4a16fb
	adpcm->states = malloc(adpcm->plug.gen.slave->channels * sizeof(*adpcm->states));
Packit 4a16fb
	if (adpcm->states == NULL)
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_adpcm_hw_free(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_adpcm_t *adpcm = pcm->private_data;
Packit 4a16fb
	free(adpcm->states);
Packit 4a16fb
	adpcm->states = NULL;
Packit 4a16fb
	return snd_pcm_hw_free(adpcm->plug.gen.slave);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_adpcm_init(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_adpcm_t *adpcm = pcm->private_data;
Packit 4a16fb
	unsigned int k;
Packit 4a16fb
	for (k = 0; k < pcm->channels; ++k) {
Packit 4a16fb
		adpcm->states[k].pred_val = 0;
Packit 4a16fb
		adpcm->states[k].step_idx = 0;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_uframes_t
Packit 4a16fb
snd_pcm_adpcm_write_areas(snd_pcm_t *pcm,
Packit 4a16fb
			  const snd_pcm_channel_area_t *areas,
Packit 4a16fb
			  snd_pcm_uframes_t offset,
Packit 4a16fb
			  snd_pcm_uframes_t size,
Packit 4a16fb
			  const snd_pcm_channel_area_t *slave_areas,
Packit 4a16fb
			  snd_pcm_uframes_t slave_offset,
Packit 4a16fb
			  snd_pcm_uframes_t *slave_sizep)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_adpcm_t *adpcm = pcm->private_data;
Packit 4a16fb
	if (size > *slave_sizep)
Packit 4a16fb
		size = *slave_sizep;
Packit 4a16fb
	adpcm->func(slave_areas, slave_offset,
Packit 4a16fb
		    areas, offset, 
Packit 4a16fb
		    pcm->channels, size,
Packit 4a16fb
		    adpcm->getput_idx, adpcm->states);
Packit 4a16fb
	*slave_sizep = size;
Packit 4a16fb
	return size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_uframes_t
Packit 4a16fb
snd_pcm_adpcm_read_areas(snd_pcm_t *pcm,
Packit 4a16fb
			 const snd_pcm_channel_area_t *areas,
Packit 4a16fb
			 snd_pcm_uframes_t offset,
Packit 4a16fb
			 snd_pcm_uframes_t size,
Packit 4a16fb
			 const snd_pcm_channel_area_t *slave_areas,
Packit 4a16fb
			 snd_pcm_uframes_t slave_offset,
Packit 4a16fb
			 snd_pcm_uframes_t *slave_sizep)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_adpcm_t *adpcm = pcm->private_data;
Packit 4a16fb
	if (size > *slave_sizep)
Packit 4a16fb
		size = *slave_sizep;
Packit 4a16fb
	adpcm->func(areas, offset, 
Packit 4a16fb
		    slave_areas, slave_offset,
Packit 4a16fb
		    pcm->channels, size,
Packit 4a16fb
		    adpcm->getput_idx, adpcm->states);
Packit 4a16fb
	*slave_sizep = size;
Packit 4a16fb
	return size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_adpcm_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_adpcm_t *adpcm = pcm->private_data;
Packit 4a16fb
	snd_output_printf(out, "Ima-ADPCM conversion PCM (%s)\n", 
Packit 4a16fb
		snd_pcm_format_name(adpcm->sformat));
Packit 4a16fb
	if (pcm->setup) {
Packit 4a16fb
		snd_output_printf(out, "Its setup is:\n");
Packit 4a16fb
		snd_pcm_dump_setup(pcm, out);
Packit 4a16fb
	}
Packit 4a16fb
	snd_output_printf(out, "Slave: ");
Packit 4a16fb
	snd_pcm_dump(adpcm->plug.gen.slave, out);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static const snd_pcm_ops_t snd_pcm_adpcm_ops = {
Packit 4a16fb
	.close = snd_pcm_generic_close,
Packit 4a16fb
	.info = snd_pcm_generic_info,
Packit 4a16fb
	.hw_refine = snd_pcm_adpcm_hw_refine,
Packit 4a16fb
	.hw_params = snd_pcm_adpcm_hw_params,
Packit 4a16fb
	.hw_free = snd_pcm_adpcm_hw_free,
Packit 4a16fb
	.sw_params = snd_pcm_generic_sw_params,
Packit 4a16fb
	.channel_info = snd_pcm_generic_channel_info,
Packit 4a16fb
	.dump = snd_pcm_adpcm_dump,
Packit 4a16fb
	.nonblock = snd_pcm_generic_nonblock,
Packit 4a16fb
	.async = snd_pcm_generic_async,
Packit 4a16fb
	.mmap = snd_pcm_generic_mmap,
Packit 4a16fb
	.munmap = snd_pcm_generic_munmap,
Packit 4a16fb
	.query_chmaps = snd_pcm_generic_query_chmaps,
Packit 4a16fb
	.get_chmap = snd_pcm_generic_get_chmap,
Packit 4a16fb
	.set_chmap = snd_pcm_generic_set_chmap,
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Creates a new Ima-ADPCM conversion PCM
Packit 4a16fb
 * \param pcmp Returns created PCM handle
Packit 4a16fb
 * \param name Name of PCM
Packit 4a16fb
 * \param sformat Slave (destination) format
Packit 4a16fb
 * \param slave Slave PCM handle
Packit 4a16fb
 * \param close_slave When set, the slave PCM handle is closed with copy PCM
Packit 4a16fb
 * \retval zero on success otherwise a negative error code
Packit 4a16fb
 * \warning Using of this function might be dangerous in the sense
Packit 4a16fb
 *          of compatibility reasons. The prototype might be freely
Packit 4a16fb
 *          changed in future.
Packit 4a16fb
 */
Packit 4a16fb
int snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_t *pcm;
Packit 4a16fb
	snd_pcm_adpcm_t *adpcm;
Packit 4a16fb
	int err;
Packit 4a16fb
	assert(pcmp && slave);
Packit 4a16fb
	if (snd_pcm_format_linear(sformat) != 1 &&
Packit 4a16fb
	    sformat != SND_PCM_FORMAT_IMA_ADPCM)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	adpcm = calloc(1, sizeof(snd_pcm_adpcm_t));
Packit 4a16fb
	if (!adpcm) {
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	}
Packit 4a16fb
	adpcm->sformat = sformat;
Packit 4a16fb
	snd_pcm_plugin_init(&adpcm->plug);
Packit 4a16fb
	adpcm->plug.read = snd_pcm_adpcm_read_areas;
Packit 4a16fb
	adpcm->plug.write = snd_pcm_adpcm_write_areas;
Packit 4a16fb
	adpcm->plug.init = snd_pcm_adpcm_init;
Packit 4a16fb
	adpcm->plug.gen.slave = slave;
Packit 4a16fb
	adpcm->plug.gen.close_slave = close_slave;
Packit 4a16fb
Packit 4a16fb
	err = snd_pcm_new(&pcm, SND_PCM_TYPE_ADPCM, name, slave->stream, slave->mode);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		free(adpcm);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	pcm->ops = &snd_pcm_adpcm_ops;
Packit 4a16fb
	pcm->fast_ops = &snd_pcm_plugin_fast_ops;
Packit 4a16fb
	pcm->private_data = adpcm;
Packit 4a16fb
	pcm->poll_fd = slave->poll_fd;
Packit 4a16fb
	pcm->poll_events = slave->poll_events;
Packit 4a16fb
	pcm->tstamp_type = slave->tstamp_type;
Packit 4a16fb
	snd_pcm_set_hw_ptr(pcm, &adpcm->plug.hw_ptr, -1, 0);
Packit 4a16fb
	snd_pcm_set_appl_ptr(pcm, &adpcm->plug.appl_ptr, -1, 0);
Packit 4a16fb
	*pcmp = pcm;
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/*! \page pcm_plugins
Packit 4a16fb
Packit 4a16fb
\section pcm_plugins_adpcm Plugin: Ima-ADPCM
Packit 4a16fb
Packit 4a16fb
This plugin converts Ima-ADPCM samples to linear or linear to Ima-ADPCM samples
Packit 4a16fb
from master Ima-ADPCM conversion PCM to given slave PCM. The channel count,
Packit 4a16fb
format and rate must match for both of them.
Packit 4a16fb
Packit 4a16fb
\code
Packit 4a16fb
pcm.name {
Packit 4a16fb
        type adpcm              # Ima-ADPCM conversion PCM
Packit 4a16fb
        slave STR               # Slave name
Packit 4a16fb
        # or
Packit 4a16fb
        slave {                 # Slave definition
Packit 4a16fb
                pcm STR         # Slave PCM name
Packit 4a16fb
                # or
Packit 4a16fb
                pcm { }         # Slave PCM definition
Packit 4a16fb
                format STR      # Slave format
Packit 4a16fb
        }
Packit 4a16fb
}
Packit 4a16fb
\endcode
Packit 4a16fb
Packit 4a16fb
\subsection pcm_plugins_adpcm_funcref Function reference
Packit 4a16fb
Packit 4a16fb
    Packit 4a16fb
      
  • snd_pcm_adpcm_open()
  • Packit 4a16fb
      
  • _snd_pcm_adpcm_open()
  • Packit 4a16fb
    Packit 4a16fb
    Packit 4a16fb
    */
    Packit 4a16fb
    Packit 4a16fb
    /**
    Packit 4a16fb
     * \brief Creates a new Ima-ADPCM conversion PCM
    Packit 4a16fb
     * \param pcmp Returns created PCM handle
    Packit 4a16fb
     * \param name Name of PCM
    Packit 4a16fb
     * \param root Root configuration node
    Packit 4a16fb
     * \param conf Configuration node with copy PCM description
    Packit 4a16fb
     * \param stream Stream type
    Packit 4a16fb
     * \param mode Stream mode
    Packit 4a16fb
     * \retval zero on success otherwise a negative error code
    Packit 4a16fb
     * \warning Using of this function might be dangerous in the sense
    Packit 4a16fb
     *          of compatibility reasons. The prototype might be freely
    Packit 4a16fb
     *          changed in future.
    Packit 4a16fb
     */
    Packit 4a16fb
    int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name,
    Packit 4a16fb
    			snd_config_t *root, snd_config_t *conf, 
    Packit 4a16fb
    			snd_pcm_stream_t stream, int mode)
    Packit 4a16fb
    {
    Packit 4a16fb
    	snd_config_iterator_t i, next;
    Packit 4a16fb
    	int err;
    Packit 4a16fb
    	snd_pcm_t *spcm;
    Packit 4a16fb
    	snd_config_t *slave = NULL, *sconf;
    Packit 4a16fb
    	snd_pcm_format_t sformat;
    Packit 4a16fb
    	snd_config_for_each(i, next, conf) {
    Packit 4a16fb
    		snd_config_t *n = snd_config_iterator_entry(i);
    Packit 4a16fb
    		const char *id;
    Packit 4a16fb
    		if (snd_config_get_id(n, &id) < 0)
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		if (snd_pcm_conf_generic_id(id))
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		if (strcmp(id, "slave") == 0) {
    Packit 4a16fb
    			slave = n;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		SNDERR("Unknown field %s", id);
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	if (!slave) {
    Packit 4a16fb
    		SNDERR("slave is not defined");
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	err = snd_pcm_slave_conf(root, slave, &sconf, 1,
    Packit 4a16fb
    				 SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
    Packit 4a16fb
    	if (err < 0)
    Packit 4a16fb
    		return err;
    Packit 4a16fb
    	if (snd_pcm_format_linear(sformat) != 1 &&
    Packit 4a16fb
    	    sformat != SND_PCM_FORMAT_IMA_ADPCM) {
    Packit 4a16fb
    	    	snd_config_delete(sconf);
    Packit 4a16fb
    		SNDERR("invalid slave format");
    Packit 4a16fb
    		return -EINVAL;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
    Packit 4a16fb
    	snd_config_delete(sconf);
    Packit 4a16fb
    	if (err < 0)
    Packit 4a16fb
    		return err;
    Packit 4a16fb
    	err = snd_pcm_adpcm_open(pcmp, name, sformat, spcm, 1);
    Packit 4a16fb
    	if (err < 0)
    Packit 4a16fb
    		snd_pcm_close(spcm);
    Packit 4a16fb
    	return err;
    Packit 4a16fb
    }
    Packit 4a16fb
    #ifndef DOC_HIDDEN
    Packit 4a16fb
    SND_DLSYM_BUILD_VERSION(_snd_pcm_adpcm_open, SND_PCM_DLSYM_VERSION);
    Packit 4a16fb
    #endif