162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  Mu-Law conversion Plug-In Interface
362306a36Sopenharmony_ci *  Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
462306a36Sopenharmony_ci *                        Uros Bizjak <uros@kss-loka.si>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Based on reference implementation by Sun Microsystems, Inc.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
962306a36Sopenharmony_ci *   it under the terms of the GNU Library General Public License as
1062306a36Sopenharmony_ci *   published by the Free Software Foundation; either version 2 of
1162306a36Sopenharmony_ci *   the License, or (at your option) any later version.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
1462306a36Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
1562306a36Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1662306a36Sopenharmony_ci *   GNU Library General Public License for more details.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *   You should have received a copy of the GNU Library General Public
1962306a36Sopenharmony_ci *   License along with this library; if not, write to the Free Software
2062306a36Sopenharmony_ci *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/time.h>
2562306a36Sopenharmony_ci#include <sound/core.h>
2662306a36Sopenharmony_ci#include <sound/pcm.h>
2762306a36Sopenharmony_ci#include "pcm_plugin.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define	SIGN_BIT	(0x80)		/* Sign bit for a u-law byte. */
3062306a36Sopenharmony_ci#define	QUANT_MASK	(0xf)		/* Quantization field mask. */
3162306a36Sopenharmony_ci#define	NSEGS		(8)		/* Number of u-law segments. */
3262306a36Sopenharmony_ci#define	SEG_SHIFT	(4)		/* Left shift for segment number. */
3362306a36Sopenharmony_ci#define	SEG_MASK	(0x70)		/* Segment field mask. */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic inline int val_seg(int val)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	int r = 0;
3862306a36Sopenharmony_ci	val >>= 7;
3962306a36Sopenharmony_ci	if (val & 0xf0) {
4062306a36Sopenharmony_ci		val >>= 4;
4162306a36Sopenharmony_ci		r += 4;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci	if (val & 0x0c) {
4462306a36Sopenharmony_ci		val >>= 2;
4562306a36Sopenharmony_ci		r += 2;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci	if (val & 0x02)
4862306a36Sopenharmony_ci		r += 1;
4962306a36Sopenharmony_ci	return r;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define	BIAS		(0x84)		/* Bias for linear code. */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/*
5562306a36Sopenharmony_ci * linear2ulaw() - Convert a linear PCM value to u-law
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci * In order to simplify the encoding process, the original linear magnitude
5862306a36Sopenharmony_ci * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
5962306a36Sopenharmony_ci * (33 - 8191). The result can be seen in the following encoding table:
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci *	Biased Linear Input Code	Compressed Code
6262306a36Sopenharmony_ci *	------------------------	---------------
6362306a36Sopenharmony_ci *	00000001wxyza			000wxyz
6462306a36Sopenharmony_ci *	0000001wxyzab			001wxyz
6562306a36Sopenharmony_ci *	000001wxyzabc			010wxyz
6662306a36Sopenharmony_ci *	00001wxyzabcd			011wxyz
6762306a36Sopenharmony_ci *	0001wxyzabcde			100wxyz
6862306a36Sopenharmony_ci *	001wxyzabcdef			101wxyz
6962306a36Sopenharmony_ci *	01wxyzabcdefg			110wxyz
7062306a36Sopenharmony_ci *	1wxyzabcdefgh			111wxyz
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * Each biased linear code has a leading 1 which identifies the segment
7362306a36Sopenharmony_ci * number. The value of the segment number is equal to 7 minus the number
7462306a36Sopenharmony_ci * of leading 0's. The quantization interval is directly available as the
7562306a36Sopenharmony_ci * four bits wxyz.  * The trailing bits (a - h) are ignored.
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * Ordinarily the complement of the resulting code word is used for
7862306a36Sopenharmony_ci * transmission, and so the code word is complemented before it is returned.
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci * For further information see John C. Bellamy's Digital Telephony, 1982,
8162306a36Sopenharmony_ci * John Wiley & Sons, pps 98-111 and 472-476.
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_cistatic unsigned char linear2ulaw(int pcm_val)	/* 2's complement (16-bit range) */
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	int mask;
8662306a36Sopenharmony_ci	int seg;
8762306a36Sopenharmony_ci	unsigned char uval;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* Get the sign and the magnitude of the value. */
9062306a36Sopenharmony_ci	if (pcm_val < 0) {
9162306a36Sopenharmony_ci		pcm_val = BIAS - pcm_val;
9262306a36Sopenharmony_ci		mask = 0x7F;
9362306a36Sopenharmony_ci	} else {
9462306a36Sopenharmony_ci		pcm_val += BIAS;
9562306a36Sopenharmony_ci		mask = 0xFF;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	if (pcm_val > 0x7FFF)
9862306a36Sopenharmony_ci		pcm_val = 0x7FFF;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* Convert the scaled magnitude to segment number. */
10162306a36Sopenharmony_ci	seg = val_seg(pcm_val);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/*
10462306a36Sopenharmony_ci	 * Combine the sign, segment, quantization bits;
10562306a36Sopenharmony_ci	 * and complement the code word.
10662306a36Sopenharmony_ci	 */
10762306a36Sopenharmony_ci	uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
10862306a36Sopenharmony_ci	return uval ^ mask;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*
11262306a36Sopenharmony_ci * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
11362306a36Sopenharmony_ci *
11462306a36Sopenharmony_ci * First, a biased linear code is derived from the code word. An unbiased
11562306a36Sopenharmony_ci * output can then be obtained by subtracting 33 from the biased code.
11662306a36Sopenharmony_ci *
11762306a36Sopenharmony_ci * Note that this function expects to be passed the complement of the
11862306a36Sopenharmony_ci * original code word. This is in keeping with ISDN conventions.
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistatic int ulaw2linear(unsigned char u_val)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	int t;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* Complement to obtain normal u-law value. */
12562306a36Sopenharmony_ci	u_val = ~u_val;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/*
12862306a36Sopenharmony_ci	 * Extract and bias the quantization bits. Then
12962306a36Sopenharmony_ci	 * shift up by the segment number and subtract out the bias.
13062306a36Sopenharmony_ci	 */
13162306a36Sopenharmony_ci	t = ((u_val & QUANT_MASK) << 3) + BIAS;
13262306a36Sopenharmony_ci	t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/*
13862306a36Sopenharmony_ci *  Basic Mu-Law plugin
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_citypedef void (*mulaw_f)(struct snd_pcm_plugin *plugin,
14262306a36Sopenharmony_ci			const struct snd_pcm_plugin_channel *src_channels,
14362306a36Sopenharmony_ci			struct snd_pcm_plugin_channel *dst_channels,
14462306a36Sopenharmony_ci			snd_pcm_uframes_t frames);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistruct mulaw_priv {
14762306a36Sopenharmony_ci	mulaw_f func;
14862306a36Sopenharmony_ci	int cvt_endian;			/* need endian conversion? */
14962306a36Sopenharmony_ci	unsigned int native_ofs;	/* byte offset in native format */
15062306a36Sopenharmony_ci	unsigned int copy_ofs;		/* byte offset in s16 format */
15162306a36Sopenharmony_ci	unsigned int native_bytes;	/* byte size of the native format */
15262306a36Sopenharmony_ci	unsigned int copy_bytes;	/* bytes to copy per conversion */
15362306a36Sopenharmony_ci	u16 flip; /* MSB flip for signedness, done after endian conversion */
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic inline void cvt_s16_to_native(struct mulaw_priv *data,
15762306a36Sopenharmony_ci				     unsigned char *dst, u16 sample)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	sample ^= data->flip;
16062306a36Sopenharmony_ci	if (data->cvt_endian)
16162306a36Sopenharmony_ci		sample = swab16(sample);
16262306a36Sopenharmony_ci	if (data->native_bytes > data->copy_bytes)
16362306a36Sopenharmony_ci		memset(dst, 0, data->native_bytes);
16462306a36Sopenharmony_ci	memcpy(dst + data->native_ofs, (char *)&sample + data->copy_ofs,
16562306a36Sopenharmony_ci	       data->copy_bytes);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void mulaw_decode(struct snd_pcm_plugin *plugin,
16962306a36Sopenharmony_ci			const struct snd_pcm_plugin_channel *src_channels,
17062306a36Sopenharmony_ci			struct snd_pcm_plugin_channel *dst_channels,
17162306a36Sopenharmony_ci			snd_pcm_uframes_t frames)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data;
17462306a36Sopenharmony_ci	int channel;
17562306a36Sopenharmony_ci	int nchannels = plugin->src_format.channels;
17662306a36Sopenharmony_ci	for (channel = 0; channel < nchannels; ++channel) {
17762306a36Sopenharmony_ci		char *src;
17862306a36Sopenharmony_ci		char *dst;
17962306a36Sopenharmony_ci		int src_step, dst_step;
18062306a36Sopenharmony_ci		snd_pcm_uframes_t frames1;
18162306a36Sopenharmony_ci		if (!src_channels[channel].enabled) {
18262306a36Sopenharmony_ci			if (dst_channels[channel].wanted)
18362306a36Sopenharmony_ci				snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
18462306a36Sopenharmony_ci			dst_channels[channel].enabled = 0;
18562306a36Sopenharmony_ci			continue;
18662306a36Sopenharmony_ci		}
18762306a36Sopenharmony_ci		dst_channels[channel].enabled = 1;
18862306a36Sopenharmony_ci		src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
18962306a36Sopenharmony_ci		dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
19062306a36Sopenharmony_ci		src_step = src_channels[channel].area.step / 8;
19162306a36Sopenharmony_ci		dst_step = dst_channels[channel].area.step / 8;
19262306a36Sopenharmony_ci		frames1 = frames;
19362306a36Sopenharmony_ci		while (frames1-- > 0) {
19462306a36Sopenharmony_ci			signed short sample = ulaw2linear(*src);
19562306a36Sopenharmony_ci			cvt_s16_to_native(data, dst, sample);
19662306a36Sopenharmony_ci			src += src_step;
19762306a36Sopenharmony_ci			dst += dst_step;
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic inline signed short cvt_native_to_s16(struct mulaw_priv *data,
20362306a36Sopenharmony_ci					     unsigned char *src)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	u16 sample = 0;
20662306a36Sopenharmony_ci	memcpy((char *)&sample + data->copy_ofs, src + data->native_ofs,
20762306a36Sopenharmony_ci	       data->copy_bytes);
20862306a36Sopenharmony_ci	if (data->cvt_endian)
20962306a36Sopenharmony_ci		sample = swab16(sample);
21062306a36Sopenharmony_ci	sample ^= data->flip;
21162306a36Sopenharmony_ci	return (signed short)sample;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic void mulaw_encode(struct snd_pcm_plugin *plugin,
21562306a36Sopenharmony_ci			const struct snd_pcm_plugin_channel *src_channels,
21662306a36Sopenharmony_ci			struct snd_pcm_plugin_channel *dst_channels,
21762306a36Sopenharmony_ci			snd_pcm_uframes_t frames)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data;
22062306a36Sopenharmony_ci	int channel;
22162306a36Sopenharmony_ci	int nchannels = plugin->src_format.channels;
22262306a36Sopenharmony_ci	for (channel = 0; channel < nchannels; ++channel) {
22362306a36Sopenharmony_ci		char *src;
22462306a36Sopenharmony_ci		char *dst;
22562306a36Sopenharmony_ci		int src_step, dst_step;
22662306a36Sopenharmony_ci		snd_pcm_uframes_t frames1;
22762306a36Sopenharmony_ci		if (!src_channels[channel].enabled) {
22862306a36Sopenharmony_ci			if (dst_channels[channel].wanted)
22962306a36Sopenharmony_ci				snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format);
23062306a36Sopenharmony_ci			dst_channels[channel].enabled = 0;
23162306a36Sopenharmony_ci			continue;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci		dst_channels[channel].enabled = 1;
23462306a36Sopenharmony_ci		src = src_channels[channel].area.addr + src_channels[channel].area.first / 8;
23562306a36Sopenharmony_ci		dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8;
23662306a36Sopenharmony_ci		src_step = src_channels[channel].area.step / 8;
23762306a36Sopenharmony_ci		dst_step = dst_channels[channel].area.step / 8;
23862306a36Sopenharmony_ci		frames1 = frames;
23962306a36Sopenharmony_ci		while (frames1-- > 0) {
24062306a36Sopenharmony_ci			signed short sample = cvt_native_to_s16(data, src);
24162306a36Sopenharmony_ci			*dst = linear2ulaw(sample);
24262306a36Sopenharmony_ci			src += src_step;
24362306a36Sopenharmony_ci			dst += dst_step;
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
24962306a36Sopenharmony_ci			      const struct snd_pcm_plugin_channel *src_channels,
25062306a36Sopenharmony_ci			      struct snd_pcm_plugin_channel *dst_channels,
25162306a36Sopenharmony_ci			      snd_pcm_uframes_t frames)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct mulaw_priv *data;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
25662306a36Sopenharmony_ci		return -ENXIO;
25762306a36Sopenharmony_ci	if (frames == 0)
25862306a36Sopenharmony_ci		return 0;
25962306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
26062306a36Sopenharmony_ci	{
26162306a36Sopenharmony_ci		unsigned int channel;
26262306a36Sopenharmony_ci		for (channel = 0; channel < plugin->src_format.channels; channel++) {
26362306a36Sopenharmony_ci			if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
26462306a36Sopenharmony_ci				       src_channels[channel].area.step % 8))
26562306a36Sopenharmony_ci				return -ENXIO;
26662306a36Sopenharmony_ci			if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
26762306a36Sopenharmony_ci				       dst_channels[channel].area.step % 8))
26862306a36Sopenharmony_ci				return -ENXIO;
26962306a36Sopenharmony_ci		}
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci#endif
27262306a36Sopenharmony_ci	if (frames > dst_channels[0].frames)
27362306a36Sopenharmony_ci		frames = dst_channels[0].frames;
27462306a36Sopenharmony_ci	data = (struct mulaw_priv *)plugin->extra_data;
27562306a36Sopenharmony_ci	data->func(plugin, src_channels, dst_channels, frames);
27662306a36Sopenharmony_ci	return frames;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic void init_data(struct mulaw_priv *data, snd_pcm_format_t format)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci#ifdef SNDRV_LITTLE_ENDIAN
28262306a36Sopenharmony_ci	data->cvt_endian = snd_pcm_format_big_endian(format) > 0;
28362306a36Sopenharmony_ci#else
28462306a36Sopenharmony_ci	data->cvt_endian = snd_pcm_format_little_endian(format) > 0;
28562306a36Sopenharmony_ci#endif
28662306a36Sopenharmony_ci	if (!snd_pcm_format_signed(format))
28762306a36Sopenharmony_ci		data->flip = 0x8000;
28862306a36Sopenharmony_ci	data->native_bytes = snd_pcm_format_physical_width(format) / 8;
28962306a36Sopenharmony_ci	data->copy_bytes = data->native_bytes < 2 ? 1 : 2;
29062306a36Sopenharmony_ci	if (snd_pcm_format_little_endian(format)) {
29162306a36Sopenharmony_ci		data->native_ofs = data->native_bytes - data->copy_bytes;
29262306a36Sopenharmony_ci		data->copy_ofs = 2 - data->copy_bytes;
29362306a36Sopenharmony_ci	} else {
29462306a36Sopenharmony_ci		/* S24 in 4bytes need an 1 byte offset */
29562306a36Sopenharmony_ci		data->native_ofs = data->native_bytes -
29662306a36Sopenharmony_ci			snd_pcm_format_width(format) / 8;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ciint snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
30162306a36Sopenharmony_ci			       struct snd_pcm_plugin_format *src_format,
30262306a36Sopenharmony_ci			       struct snd_pcm_plugin_format *dst_format,
30362306a36Sopenharmony_ci			       struct snd_pcm_plugin **r_plugin)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	int err;
30662306a36Sopenharmony_ci	struct mulaw_priv *data;
30762306a36Sopenharmony_ci	struct snd_pcm_plugin *plugin;
30862306a36Sopenharmony_ci	struct snd_pcm_plugin_format *format;
30962306a36Sopenharmony_ci	mulaw_f func;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (snd_BUG_ON(!r_plugin))
31262306a36Sopenharmony_ci		return -ENXIO;
31362306a36Sopenharmony_ci	*r_plugin = NULL;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (snd_BUG_ON(src_format->rate != dst_format->rate))
31662306a36Sopenharmony_ci		return -ENXIO;
31762306a36Sopenharmony_ci	if (snd_BUG_ON(src_format->channels != dst_format->channels))
31862306a36Sopenharmony_ci		return -ENXIO;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
32162306a36Sopenharmony_ci		format = src_format;
32262306a36Sopenharmony_ci		func = mulaw_encode;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci	else if (src_format->format == SNDRV_PCM_FORMAT_MU_LAW) {
32562306a36Sopenharmony_ci		format = dst_format;
32662306a36Sopenharmony_ci		func = mulaw_decode;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	else {
32962306a36Sopenharmony_ci		snd_BUG();
33062306a36Sopenharmony_ci		return -EINVAL;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci	if (!snd_pcm_format_linear(format->format))
33362306a36Sopenharmony_ci		return -EINVAL;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
33662306a36Sopenharmony_ci				   src_format, dst_format,
33762306a36Sopenharmony_ci				   sizeof(struct mulaw_priv), &plugin);
33862306a36Sopenharmony_ci	if (err < 0)
33962306a36Sopenharmony_ci		return err;
34062306a36Sopenharmony_ci	data = (struct mulaw_priv *)plugin->extra_data;
34162306a36Sopenharmony_ci	data->func = func;
34262306a36Sopenharmony_ci	init_data(data, format->format);
34362306a36Sopenharmony_ci	plugin->transfer = mulaw_transfer;
34462306a36Sopenharmony_ci	*r_plugin = plugin;
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
347