2004-11-07 19:04:02 +01:00
|
|
|
/*
|
|
|
|
* QEMU Mixing engine
|
|
|
|
*
|
2005-10-30 19:58:22 +01:00
|
|
|
* Copyright (c) 2004-2005 Vassili Karpov (malc)
|
2004-11-07 19:04:02 +01:00
|
|
|
* Copyright (c) 1998 Fabrice Bellard
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
2016-01-18 18:33:52 +01:00
|
|
|
#include "qemu/osdep.h"
|
2007-11-17 18:14:51 +01:00
|
|
|
#include "qemu-common.h"
|
2016-03-15 17:22:36 +01:00
|
|
|
#include "qemu/bswap.h"
|
2017-02-02 06:50:54 +01:00
|
|
|
#include "qemu/error-report.h"
|
2007-11-17 18:14:51 +01:00
|
|
|
#include "audio.h"
|
2004-11-07 19:04:02 +01:00
|
|
|
|
2005-10-30 19:58:22 +01:00
|
|
|
#define AUDIO_CAP "mixeng"
|
|
|
|
#include "audio_int.h"
|
|
|
|
|
|
|
|
/* 8 bit */
|
|
|
|
#define ENDIAN_CONVERSION natural
|
|
|
|
#define ENDIAN_CONVERT(v) (v)
|
|
|
|
|
|
|
|
/* Signed 8 bit */
|
2012-05-18 13:08:14 +02:00
|
|
|
#define BSIZE 8
|
|
|
|
#define ITYPE int
|
2005-10-30 19:58:22 +01:00
|
|
|
#define IN_MIN SCHAR_MIN
|
|
|
|
#define IN_MAX SCHAR_MAX
|
2004-11-07 19:04:02 +01:00
|
|
|
#define SIGNED
|
2005-10-30 19:58:22 +01:00
|
|
|
#define SHIFT 8
|
2004-11-07 19:04:02 +01:00
|
|
|
#include "mixeng_template.h"
|
|
|
|
#undef SIGNED
|
|
|
|
#undef IN_MAX
|
|
|
|
#undef IN_MIN
|
2012-05-18 13:08:14 +02:00
|
|
|
#undef BSIZE
|
|
|
|
#undef ITYPE
|
2005-10-30 19:58:22 +01:00
|
|
|
#undef SHIFT
|
2004-11-07 19:04:02 +01:00
|
|
|
|
2005-10-30 19:58:22 +01:00
|
|
|
/* Unsigned 8 bit */
|
2012-05-18 13:08:14 +02:00
|
|
|
#define BSIZE 8
|
|
|
|
#define ITYPE uint
|
2004-11-07 19:04:02 +01:00
|
|
|
#define IN_MIN 0
|
|
|
|
#define IN_MAX UCHAR_MAX
|
2005-10-30 19:58:22 +01:00
|
|
|
#define SHIFT 8
|
2004-11-07 19:04:02 +01:00
|
|
|
#include "mixeng_template.h"
|
|
|
|
#undef IN_MAX
|
|
|
|
#undef IN_MIN
|
2012-05-18 13:08:14 +02:00
|
|
|
#undef BSIZE
|
|
|
|
#undef ITYPE
|
2005-10-30 19:58:22 +01:00
|
|
|
#undef SHIFT
|
|
|
|
|
|
|
|
#undef ENDIAN_CONVERT
|
|
|
|
#undef ENDIAN_CONVERSION
|
2004-11-07 19:04:02 +01:00
|
|
|
|
2005-10-30 19:58:22 +01:00
|
|
|
/* Signed 16 bit */
|
2012-05-18 13:08:14 +02:00
|
|
|
#define BSIZE 16
|
|
|
|
#define ITYPE int
|
2004-11-07 19:04:02 +01:00
|
|
|
#define IN_MIN SHRT_MIN
|
|
|
|
#define IN_MAX SHRT_MAX
|
|
|
|
#define SIGNED
|
2005-10-30 19:58:22 +01:00
|
|
|
#define SHIFT 16
|
|
|
|
#define ENDIAN_CONVERSION natural
|
|
|
|
#define ENDIAN_CONVERT(v) (v)
|
2004-11-07 19:04:02 +01:00
|
|
|
#include "mixeng_template.h"
|
2005-10-30 19:58:22 +01:00
|
|
|
#undef ENDIAN_CONVERT
|
|
|
|
#undef ENDIAN_CONVERSION
|
|
|
|
#define ENDIAN_CONVERSION swap
|
|
|
|
#define ENDIAN_CONVERT(v) bswap16 (v)
|
|
|
|
#include "mixeng_template.h"
|
|
|
|
#undef ENDIAN_CONVERT
|
|
|
|
#undef ENDIAN_CONVERSION
|
2004-11-07 19:04:02 +01:00
|
|
|
#undef SIGNED
|
|
|
|
#undef IN_MAX
|
|
|
|
#undef IN_MIN
|
2012-05-18 13:08:14 +02:00
|
|
|
#undef BSIZE
|
|
|
|
#undef ITYPE
|
2005-10-30 19:58:22 +01:00
|
|
|
#undef SHIFT
|
2004-11-07 19:04:02 +01:00
|
|
|
|
2007-02-17 23:19:29 +01:00
|
|
|
/* Unsigned 16 bit */
|
2012-05-18 13:08:14 +02:00
|
|
|
#define BSIZE 16
|
|
|
|
#define ITYPE uint
|
2004-11-07 19:04:02 +01:00
|
|
|
#define IN_MIN 0
|
|
|
|
#define IN_MAX USHRT_MAX
|
2005-10-30 19:58:22 +01:00
|
|
|
#define SHIFT 16
|
|
|
|
#define ENDIAN_CONVERSION natural
|
|
|
|
#define ENDIAN_CONVERT(v) (v)
|
|
|
|
#include "mixeng_template.h"
|
|
|
|
#undef ENDIAN_CONVERT
|
|
|
|
#undef ENDIAN_CONVERSION
|
|
|
|
#define ENDIAN_CONVERSION swap
|
|
|
|
#define ENDIAN_CONVERT(v) bswap16 (v)
|
2004-11-07 19:04:02 +01:00
|
|
|
#include "mixeng_template.h"
|
2005-10-30 19:58:22 +01:00
|
|
|
#undef ENDIAN_CONVERT
|
|
|
|
#undef ENDIAN_CONVERSION
|
2004-11-07 19:04:02 +01:00
|
|
|
#undef IN_MAX
|
|
|
|
#undef IN_MIN
|
2012-05-18 13:08:14 +02:00
|
|
|
#undef BSIZE
|
|
|
|
#undef ITYPE
|
2005-10-30 19:58:22 +01:00
|
|
|
#undef SHIFT
|
2004-11-07 19:04:02 +01:00
|
|
|
|
2007-02-17 23:19:29 +01:00
|
|
|
/* Signed 32 bit */
|
2012-05-18 13:08:14 +02:00
|
|
|
#define BSIZE 32
|
|
|
|
#define ITYPE int
|
2007-02-17 23:19:29 +01:00
|
|
|
#define IN_MIN INT32_MIN
|
|
|
|
#define IN_MAX INT32_MAX
|
|
|
|
#define SIGNED
|
|
|
|
#define SHIFT 32
|
|
|
|
#define ENDIAN_CONVERSION natural
|
|
|
|
#define ENDIAN_CONVERT(v) (v)
|
|
|
|
#include "mixeng_template.h"
|
|
|
|
#undef ENDIAN_CONVERT
|
|
|
|
#undef ENDIAN_CONVERSION
|
|
|
|
#define ENDIAN_CONVERSION swap
|
|
|
|
#define ENDIAN_CONVERT(v) bswap32 (v)
|
|
|
|
#include "mixeng_template.h"
|
|
|
|
#undef ENDIAN_CONVERT
|
|
|
|
#undef ENDIAN_CONVERSION
|
|
|
|
#undef SIGNED
|
|
|
|
#undef IN_MAX
|
|
|
|
#undef IN_MIN
|
2012-05-18 13:08:14 +02:00
|
|
|
#undef BSIZE
|
|
|
|
#undef ITYPE
|
2007-02-17 23:19:29 +01:00
|
|
|
#undef SHIFT
|
|
|
|
|
2010-03-11 16:28:38 +01:00
|
|
|
/* Unsigned 32 bit */
|
2012-05-18 13:08:14 +02:00
|
|
|
#define BSIZE 32
|
|
|
|
#define ITYPE uint
|
2007-02-17 23:19:29 +01:00
|
|
|
#define IN_MIN 0
|
|
|
|
#define IN_MAX UINT32_MAX
|
|
|
|
#define SHIFT 32
|
|
|
|
#define ENDIAN_CONVERSION natural
|
|
|
|
#define ENDIAN_CONVERT(v) (v)
|
|
|
|
#include "mixeng_template.h"
|
|
|
|
#undef ENDIAN_CONVERT
|
|
|
|
#undef ENDIAN_CONVERSION
|
|
|
|
#define ENDIAN_CONVERSION swap
|
|
|
|
#define ENDIAN_CONVERT(v) bswap32 (v)
|
|
|
|
#include "mixeng_template.h"
|
|
|
|
#undef ENDIAN_CONVERT
|
|
|
|
#undef ENDIAN_CONVERSION
|
|
|
|
#undef IN_MAX
|
|
|
|
#undef IN_MIN
|
2012-05-18 13:08:14 +02:00
|
|
|
#undef BSIZE
|
|
|
|
#undef ITYPE
|
2007-02-17 23:19:29 +01:00
|
|
|
#undef SHIFT
|
|
|
|
|
|
|
|
t_sample *mixeng_conv[2][2][2][3] = {
|
2004-11-07 19:04:02 +01:00
|
|
|
{
|
|
|
|
{
|
2005-10-30 19:58:22 +01:00
|
|
|
{
|
|
|
|
conv_natural_uint8_t_to_mono,
|
2007-02-17 23:19:29 +01:00
|
|
|
conv_natural_uint16_t_to_mono,
|
|
|
|
conv_natural_uint32_t_to_mono
|
2005-10-30 19:58:22 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
conv_natural_uint8_t_to_mono,
|
2007-02-17 23:19:29 +01:00
|
|
|
conv_swap_uint16_t_to_mono,
|
|
|
|
conv_swap_uint32_t_to_mono,
|
2005-10-30 19:58:22 +01:00
|
|
|
}
|
2004-11-07 19:04:02 +01:00
|
|
|
},
|
|
|
|
{
|
2005-10-30 19:58:22 +01:00
|
|
|
{
|
|
|
|
conv_natural_int8_t_to_mono,
|
2007-02-17 23:19:29 +01:00
|
|
|
conv_natural_int16_t_to_mono,
|
|
|
|
conv_natural_int32_t_to_mono
|
2005-10-30 19:58:22 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
conv_natural_int8_t_to_mono,
|
2007-02-17 23:19:29 +01:00
|
|
|
conv_swap_int16_t_to_mono,
|
|
|
|
conv_swap_int32_t_to_mono
|
2005-10-30 19:58:22 +01:00
|
|
|
}
|
2004-11-07 19:04:02 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
2005-10-30 19:58:22 +01:00
|
|
|
{
|
|
|
|
conv_natural_uint8_t_to_stereo,
|
2007-02-17 23:19:29 +01:00
|
|
|
conv_natural_uint16_t_to_stereo,
|
|
|
|
conv_natural_uint32_t_to_stereo
|
2005-10-30 19:58:22 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
conv_natural_uint8_t_to_stereo,
|
2007-02-17 23:19:29 +01:00
|
|
|
conv_swap_uint16_t_to_stereo,
|
|
|
|
conv_swap_uint32_t_to_stereo
|
2005-10-30 19:58:22 +01:00
|
|
|
}
|
2004-11-07 19:04:02 +01:00
|
|
|
},
|
|
|
|
{
|
2005-10-30 19:58:22 +01:00
|
|
|
{
|
|
|
|
conv_natural_int8_t_to_stereo,
|
2007-02-17 23:19:29 +01:00
|
|
|
conv_natural_int16_t_to_stereo,
|
|
|
|
conv_natural_int32_t_to_stereo
|
2005-10-30 19:58:22 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
conv_natural_int8_t_to_stereo,
|
2007-02-17 23:19:29 +01:00
|
|
|
conv_swap_int16_t_to_stereo,
|
|
|
|
conv_swap_int32_t_to_stereo,
|
2005-10-30 19:58:22 +01:00
|
|
|
}
|
2004-11-07 19:04:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2007-02-17 23:19:29 +01:00
|
|
|
f_sample *mixeng_clip[2][2][2][3] = {
|
2004-11-07 19:04:02 +01:00
|
|
|
{
|
|
|
|
{
|
2005-10-30 19:58:22 +01:00
|
|
|
{
|
|
|
|
clip_natural_uint8_t_from_mono,
|
2007-02-17 23:19:29 +01:00
|
|
|
clip_natural_uint16_t_from_mono,
|
|
|
|
clip_natural_uint32_t_from_mono
|
2005-10-30 19:58:22 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
clip_natural_uint8_t_from_mono,
|
2007-02-17 23:19:29 +01:00
|
|
|
clip_swap_uint16_t_from_mono,
|
|
|
|
clip_swap_uint32_t_from_mono
|
2005-10-30 19:58:22 +01:00
|
|
|
}
|
2004-11-07 19:04:02 +01:00
|
|
|
},
|
|
|
|
{
|
2005-10-30 19:58:22 +01:00
|
|
|
{
|
|
|
|
clip_natural_int8_t_from_mono,
|
2007-02-17 23:19:29 +01:00
|
|
|
clip_natural_int16_t_from_mono,
|
|
|
|
clip_natural_int32_t_from_mono
|
2005-10-30 19:58:22 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
clip_natural_int8_t_from_mono,
|
2007-02-17 23:19:29 +01:00
|
|
|
clip_swap_int16_t_from_mono,
|
|
|
|
clip_swap_int32_t_from_mono
|
2005-10-30 19:58:22 +01:00
|
|
|
}
|
2004-11-07 19:04:02 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
{
|
2005-10-30 19:58:22 +01:00
|
|
|
{
|
|
|
|
clip_natural_uint8_t_from_stereo,
|
2007-02-17 23:19:29 +01:00
|
|
|
clip_natural_uint16_t_from_stereo,
|
|
|
|
clip_natural_uint32_t_from_stereo
|
2005-10-30 19:58:22 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
clip_natural_uint8_t_from_stereo,
|
2007-02-17 23:19:29 +01:00
|
|
|
clip_swap_uint16_t_from_stereo,
|
|
|
|
clip_swap_uint32_t_from_stereo
|
2005-10-30 19:58:22 +01:00
|
|
|
}
|
2004-11-07 19:04:02 +01:00
|
|
|
},
|
|
|
|
{
|
2005-10-30 19:58:22 +01:00
|
|
|
{
|
|
|
|
clip_natural_int8_t_from_stereo,
|
2007-02-17 23:19:29 +01:00
|
|
|
clip_natural_int16_t_from_stereo,
|
|
|
|
clip_natural_int32_t_from_stereo
|
2005-10-30 19:58:22 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
clip_natural_int8_t_from_stereo,
|
2007-02-17 23:19:29 +01:00
|
|
|
clip_swap_int16_t_from_stereo,
|
|
|
|
clip_swap_int32_t_from_stereo
|
2005-10-30 19:58:22 +01:00
|
|
|
}
|
2004-11-07 19:04:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-02-02 06:50:54 +01:00
|
|
|
|
|
|
|
void audio_sample_to_uint64(void *samples, int pos,
|
|
|
|
uint64_t *left, uint64_t *right)
|
|
|
|
{
|
|
|
|
struct st_sample *sample = samples;
|
|
|
|
sample += pos;
|
|
|
|
#ifdef FLOAT_MIXENG
|
|
|
|
error_report(
|
|
|
|
"Coreaudio and floating point samples are not supported by replay yet");
|
|
|
|
abort();
|
|
|
|
#else
|
|
|
|
*left = sample->l;
|
|
|
|
*right = sample->r;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void audio_sample_from_uint64(void *samples, int pos,
|
|
|
|
uint64_t left, uint64_t right)
|
|
|
|
{
|
|
|
|
struct st_sample *sample = samples;
|
|
|
|
sample += pos;
|
|
|
|
#ifdef FLOAT_MIXENG
|
|
|
|
error_report(
|
|
|
|
"Coreaudio and floating point samples are not supported by replay yet");
|
|
|
|
abort();
|
|
|
|
#else
|
|
|
|
sample->l = left;
|
|
|
|
sample->r = right;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2004-11-07 19:04:02 +01:00
|
|
|
/*
|
|
|
|
* August 21, 1998
|
|
|
|
* Copyright 1998 Fabrice Bellard.
|
|
|
|
*
|
2016-03-23 15:59:57 +01:00
|
|
|
* [Rewrote completely the code of Lance Norskog And Sundry
|
2004-11-07 19:04:02 +01:00
|
|
|
* Contributors with a more efficient algorithm.]
|
|
|
|
*
|
|
|
|
* This source code is freely redistributable and may be used for
|
2005-10-30 19:58:22 +01:00
|
|
|
* any purpose. This copyright notice must be maintained.
|
|
|
|
* Lance Norskog And Sundry Contributors are not responsible for
|
|
|
|
* the consequences of using this software.
|
2004-11-07 19:04:02 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sound Tools rate change effect file.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* Linear Interpolation.
|
|
|
|
*
|
|
|
|
* The use of fractional increment allows us to use no buffer. It
|
|
|
|
* avoid the problems at the end of the buffer we had with the old
|
|
|
|
* method which stored a possibly big buffer of size
|
|
|
|
* lcm(in_rate,out_rate).
|
|
|
|
*
|
|
|
|
* Limited to 16 bit samples and sampling frequency <= 65535 Hz. If
|
|
|
|
* the input & output frequencies are equal, a delay of one sample is
|
|
|
|
* introduced. Limited to processing 32-bit count worth of samples.
|
|
|
|
*
|
|
|
|
* 1 << FRAC_BITS evaluating to zero in several places. Changed with
|
|
|
|
* an (unsigned long) cast to make it safe. MarkMLl 2/1/99
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Private data */
|
2005-11-05 19:55:28 +01:00
|
|
|
struct rate {
|
2004-11-07 19:04:02 +01:00
|
|
|
uint64_t opos;
|
|
|
|
uint64_t opos_inc;
|
|
|
|
uint32_t ipos; /* position in the input stream (integer) */
|
2008-12-03 23:48:44 +01:00
|
|
|
struct st_sample ilast; /* last sample in the input stream */
|
2005-11-05 19:55:28 +01:00
|
|
|
};
|
2004-11-07 19:04:02 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare processing.
|
|
|
|
*/
|
|
|
|
void *st_rate_start (int inrate, int outrate)
|
|
|
|
{
|
2005-11-05 19:55:28 +01:00
|
|
|
struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate));
|
2004-11-07 19:04:02 +01:00
|
|
|
|
|
|
|
if (!rate) {
|
2005-11-11 01:03:36 +01:00
|
|
|
dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate));
|
2005-10-30 19:58:22 +01:00
|
|
|
return NULL;
|
2004-11-07 19:04:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
rate->opos = 0;
|
|
|
|
|
|
|
|
/* increment */
|
2005-10-30 19:58:22 +01:00
|
|
|
rate->opos_inc = ((uint64_t) inrate << 32) / outrate;
|
2004-11-07 19:04:02 +01:00
|
|
|
|
|
|
|
rate->ipos = 0;
|
|
|
|
rate->ilast.l = 0;
|
|
|
|
rate->ilast.r = 0;
|
|
|
|
return rate;
|
|
|
|
}
|
|
|
|
|
2005-10-30 19:58:22 +01:00
|
|
|
#define NAME st_rate_flow_mix
|
|
|
|
#define OP(a, b) a += b
|
|
|
|
#include "rate_template.h"
|
2004-11-07 19:04:02 +01:00
|
|
|
|
2005-10-30 19:58:22 +01:00
|
|
|
#define NAME st_rate_flow
|
|
|
|
#define OP(a, b) a = b
|
|
|
|
#include "rate_template.h"
|
2004-11-07 19:04:02 +01:00
|
|
|
|
|
|
|
void st_rate_stop (void *opaque)
|
|
|
|
{
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free (opaque);
|
2004-11-07 19:04:02 +01:00
|
|
|
}
|
2005-10-30 19:58:22 +01:00
|
|
|
|
2008-12-03 23:48:44 +01:00
|
|
|
void mixeng_clear (struct st_sample *buf, int len)
|
2005-10-30 19:58:22 +01:00
|
|
|
{
|
2008-12-03 23:48:44 +01:00
|
|
|
memset (buf, 0, len * sizeof (struct st_sample));
|
2005-10-30 19:58:22 +01:00
|
|
|
}
|
2011-01-05 01:05:47 +01:00
|
|
|
|
|
|
|
void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol)
|
|
|
|
{
|
|
|
|
if (vol->mute) {
|
|
|
|
mixeng_clear (buf, len);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (len--) {
|
|
|
|
#ifdef FLOAT_MIXENG
|
|
|
|
buf->l = buf->l * vol->l;
|
|
|
|
buf->r = buf->r * vol->r;
|
|
|
|
#else
|
|
|
|
buf->l = (buf->l * vol->l) >> 32;
|
|
|
|
buf->r = (buf->r * vol->r) >> 32;
|
|
|
|
#endif
|
|
|
|
buf += 1;
|
|
|
|
}
|
|
|
|
}
|