Blame src/pcm/pcm_adpcm.c

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