sim/sim_alsa.c: add mp3 offload playback on sim
use host libmad to simulate read DSP. Signed-off-by: qiaohaijiao1 <qiaohaijiao1@xiaomi.com>
This commit is contained in:
parent
e6ec6e1a94
commit
18bca596d4
@ -201,6 +201,7 @@ endif
|
||||
ifeq ($(CONFIG_SIM_SOUND_ALSA),y)
|
||||
CSRCS += sim_alsa.c
|
||||
STDLIBS += -lasound
|
||||
STDLIBS += -lmad
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SIM_HOSTFS),y)
|
||||
|
@ -31,17 +31,49 @@
|
||||
#include <debug.h>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <mad.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define AUDMIN(a,b) ((a) > (b) ? (b) : (a))
|
||||
#define AUDCODEC_DEC 0x01
|
||||
#define AUDCODEC_ENC 0x10
|
||||
|
||||
/****************************************************************************
|
||||
* Private Types
|
||||
****************************************************************************/
|
||||
|
||||
struct sim_codec_ops_s
|
||||
{
|
||||
uint8_t format;
|
||||
uint8_t flags;
|
||||
|
||||
/* init codec handle */
|
||||
|
||||
void *(*init)(void);
|
||||
|
||||
/* return how much samples return from deocde.
|
||||
* or encoder needed.
|
||||
* */
|
||||
|
||||
int (*get_samples)(void *handle);
|
||||
|
||||
/* perform dec or enc on [in] data with [insize] bytes
|
||||
* [out] data with [outsize] is pcm data after decode, or
|
||||
* compress data when encode.
|
||||
* return: < 0 means failed. == 0 success
|
||||
*/
|
||||
|
||||
int (*process)(void *handle, uint8_t *in, uint32_t insize,
|
||||
uint8_t **out, unsigned int *outsize);
|
||||
|
||||
/* uninit codec handle */
|
||||
|
||||
void (*uninit)(void *handle);
|
||||
};
|
||||
|
||||
struct sim_audio_s
|
||||
{
|
||||
struct audio_lowerhalf_s dev;
|
||||
@ -61,6 +93,22 @@ struct sim_audio_s
|
||||
snd_pcm_t *pcm;
|
||||
snd_mixer_t *mixer;
|
||||
snd_mixer_elem_t *volume;
|
||||
|
||||
void *codec;
|
||||
const struct sim_codec_ops_s *ops;
|
||||
};
|
||||
|
||||
struct sim_decoder_mp3_s
|
||||
{
|
||||
uint8_t *out;
|
||||
struct mad_stream stream;
|
||||
struct mad_frame frame;
|
||||
struct mad_synth synth;
|
||||
};
|
||||
|
||||
struct sim_codec_pcm_s
|
||||
{
|
||||
uint32_t frame_size;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
@ -93,6 +141,19 @@ static int sim_audio_ioctl(struct audio_lowerhalf_s *dev, int cmd,
|
||||
static int sim_audio_reserve(struct audio_lowerhalf_s *dev);
|
||||
static int sim_audio_release(struct audio_lowerhalf_s *dev);
|
||||
|
||||
static void *sim_audio_mp3_init(void);
|
||||
static int sim_audio_mp3_samples(void *handle);
|
||||
static int sim_audio_mp3_decode(void *handle,
|
||||
uint8_t *in, uint32_t insize,
|
||||
uint8_t **out, uint32_t *outsize);
|
||||
static void sim_audio_mp3_uninit(void *handle);
|
||||
|
||||
static void *sim_audio_pcm_init(void);
|
||||
static int sim_audio_pcm_process(void *handle,
|
||||
uint8_t *in, uint32_t insize,
|
||||
uint8_t **out, uint32_t *outsize);
|
||||
static void sim_audio_pcm_uninit(void *handle);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
@ -116,6 +177,26 @@ static const struct audio_ops_s g_sim_audio_ops =
|
||||
.release = sim_audio_release,
|
||||
};
|
||||
|
||||
static const struct sim_codec_ops_s g_codec_ops[] =
|
||||
{
|
||||
{
|
||||
AUDIO_FMT_PCM,
|
||||
AUDCODEC_DEC | AUDCODEC_ENC,
|
||||
sim_audio_pcm_init,
|
||||
NULL,
|
||||
sim_audio_pcm_process,
|
||||
sim_audio_pcm_uninit,
|
||||
},
|
||||
{
|
||||
AUDIO_FMT_MP3,
|
||||
AUDCODEC_DEC,
|
||||
sim_audio_mp3_init,
|
||||
sim_audio_mp3_samples,
|
||||
sim_audio_mp3_decode,
|
||||
sim_audio_mp3_uninit
|
||||
}
|
||||
};
|
||||
|
||||
static sq_queue_t g_sim_audio;
|
||||
|
||||
/****************************************************************************
|
||||
@ -211,6 +292,27 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sim_audio_config_ops(struct sim_audio_s *priv, uint8_t fmt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(g_codec_ops) / sizeof(g_codec_ops[0]); i++)
|
||||
{
|
||||
if (g_codec_ops[i].format == fmt &&
|
||||
((priv->playback && g_codec_ops[i].flags & AUDCODEC_DEC) ||
|
||||
(!priv->playback && g_codec_ops[i].flags & AUDCODEC_ENC)))
|
||||
{
|
||||
priv->ops = &g_codec_ops[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!priv->ops)
|
||||
{
|
||||
priv->ops = &g_codec_ops[0];
|
||||
}
|
||||
}
|
||||
|
||||
static int sim_audio_open(struct sim_audio_s *priv)
|
||||
{
|
||||
snd_pcm_t *pcm;
|
||||
@ -289,7 +391,12 @@ static int sim_audio_getcaps(struct audio_lowerhalf_s *dev, int type,
|
||||
AUDIO_TYPE_INPUT) |
|
||||
AUDIO_TYPE_FEATURE |
|
||||
AUDIO_TYPE_PROCESSING;
|
||||
caps->ac_format.hw = (1 << (AUDIO_FMT_PCM - 1));
|
||||
caps->ac_format.hw = (1 << (AUDIO_FMT_PCM - 1)) |
|
||||
(1 << (AUDIO_FMT_MP3 - 1));
|
||||
break;
|
||||
case AUDIO_FMT_MP3:
|
||||
caps->ac_controls.b[0] = AUDIO_SUBFMT_PCM_MP3;
|
||||
caps->ac_controls.b[1] = AUDIO_SUBFMT_END;
|
||||
break;
|
||||
default:
|
||||
caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
|
||||
@ -300,7 +407,6 @@ static int sim_audio_getcaps(struct audio_lowerhalf_s *dev, int type,
|
||||
|
||||
case AUDIO_TYPE_OUTPUT:
|
||||
case AUDIO_TYPE_INPUT:
|
||||
|
||||
caps->ac_channels = 2;
|
||||
|
||||
switch (caps->ac_subtype)
|
||||
@ -393,13 +499,13 @@ static int sim_audio_configure(struct audio_lowerhalf_s *dev,
|
||||
|
||||
case AUDIO_TYPE_OUTPUT:
|
||||
case AUDIO_TYPE_INPUT:
|
||||
|
||||
priv->sample_rate = caps->ac_controls.hw[0] |
|
||||
(caps->ac_controls.b[3] << 16);
|
||||
priv->channels = caps->ac_channels;
|
||||
priv->bps = caps->ac_controls.b[2];
|
||||
priv->frame_size = priv->bps / 8 * priv->channels;
|
||||
|
||||
sim_audio_config_ops(priv, caps->ac_subtype);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -419,6 +525,12 @@ static int sim_audio_start(struct audio_lowerhalf_s *dev)
|
||||
{
|
||||
struct sim_audio_s *priv = (struct sim_audio_s *)dev;
|
||||
|
||||
priv->codec = priv->ops->init();
|
||||
if (priv->codec == NULL)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
return sim_audio_open(priv);
|
||||
}
|
||||
|
||||
@ -447,6 +559,9 @@ static int sim_audio_stop(struct audio_lowerhalf_s *dev)
|
||||
priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
|
||||
#endif
|
||||
|
||||
priv->ops->uninit(priv->codec);
|
||||
priv->ops = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -553,6 +668,8 @@ static void sim_audio_process(struct sim_audio_s *priv)
|
||||
struct ap_buffer_s *apb;
|
||||
snd_pcm_sframes_t expect;
|
||||
snd_pcm_sframes_t avail;
|
||||
uint8_t *out = NULL;
|
||||
uint32_t outsize;
|
||||
int ret = 0;
|
||||
|
||||
if (!priv->pcm)
|
||||
@ -566,9 +683,17 @@ static void sim_audio_process(struct sim_audio_s *priv)
|
||||
return;
|
||||
}
|
||||
|
||||
expect = priv->playback ? apb->nbytes / priv->frame_size
|
||||
: AUDMIN(apb->nmaxbytes, priv->buffer_size)
|
||||
/ priv->frame_size;
|
||||
if (priv->ops->get_samples)
|
||||
{
|
||||
expect = priv->ops->get_samples(priv->codec);
|
||||
}
|
||||
else
|
||||
{
|
||||
expect = priv->playback ? apb->nbytes / priv->frame_size
|
||||
: AUDMIN(apb->nmaxbytes, priv->buffer_size)
|
||||
/ priv->frame_size;
|
||||
}
|
||||
|
||||
avail = snd_pcm_avail(priv->pcm);
|
||||
if (avail < expect)
|
||||
{
|
||||
@ -578,11 +703,32 @@ static void sim_audio_process(struct sim_audio_s *priv)
|
||||
|
||||
if (priv->playback)
|
||||
{
|
||||
ret = snd_pcm_writei(priv->pcm, apb->samp, expect);
|
||||
ret = priv->ops->process(priv->codec, apb->samp, apb->nbytes,
|
||||
&out, &outsize);
|
||||
if (ret < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ret = snd_pcm_writei(priv->pcm, out, outsize / priv->frame_size);
|
||||
ret *= priv->frame_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = snd_pcm_readi(priv->pcm, apb->samp, expect);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = priv->ops->process(priv->codec, apb->samp,
|
||||
ret * priv->frame_size, &apb->samp, &outsize);
|
||||
if (ret < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ret = outsize;
|
||||
}
|
||||
|
||||
if (ret >= 0)
|
||||
@ -591,7 +737,7 @@ static void sim_audio_process(struct sim_audio_s *priv)
|
||||
|
||||
dq_remfirst(&priv->pendq);
|
||||
|
||||
apb->nbytes = ret * priv->frame_size;
|
||||
apb->nbytes = ret;
|
||||
if (apb->flags & AUDIO_APB_FINAL)
|
||||
{
|
||||
final = true;
|
||||
@ -702,6 +848,147 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sim_audio_mp3_scale(mad_fixed_t sample)
|
||||
{
|
||||
sample += 1L << (MAD_F_FRACBITS - 16);
|
||||
|
||||
if (sample >= MAD_F_ONE)
|
||||
{
|
||||
sample = MAD_F_ONE - 1;
|
||||
}
|
||||
else if (sample < -MAD_F_ONE)
|
||||
{
|
||||
sample = -MAD_F_ONE;
|
||||
}
|
||||
|
||||
return sample >> (MAD_F_FRACBITS + 1 - 16);
|
||||
}
|
||||
|
||||
static void *sim_audio_mp3_init(void)
|
||||
{
|
||||
struct sim_decoder_mp3_s *codec;
|
||||
|
||||
codec = kmm_malloc(sizeof(struct sim_decoder_mp3_s));
|
||||
if (codec == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mad_stream_init(&codec->stream);
|
||||
mad_frame_init(&codec->frame);
|
||||
mad_synth_init(&codec->synth);
|
||||
|
||||
codec->out = kmm_malloc(sizeof(codec->synth.pcm.samples));
|
||||
if (codec->out == NULL)
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
|
||||
return codec;
|
||||
|
||||
out:
|
||||
mad_synth_finish(&(codec->synth));
|
||||
mad_frame_finish(&(codec->frame));
|
||||
mad_stream_finish(&(codec->stream));
|
||||
kmm_free(codec);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sim_audio_mp3_samples(void *handle)
|
||||
{
|
||||
struct sim_decoder_mp3_s *codec = (struct sim_decoder_mp3_s *)handle;
|
||||
|
||||
return sizeof(codec->synth.pcm.samples[0]) / sizeof(mad_fixed_t);
|
||||
}
|
||||
|
||||
static int sim_audio_mp3_decode(void *handle,
|
||||
uint8_t *in, uint32_t insize,
|
||||
uint8_t **out, uint32_t *outsize)
|
||||
{
|
||||
struct sim_decoder_mp3_s *codec = (struct sim_decoder_mp3_s *)handle;
|
||||
const mad_fixed_t *right_ch;
|
||||
const mad_fixed_t *left_ch;
|
||||
int nchannels;
|
||||
int nsamples;
|
||||
uint8_t *ptr;
|
||||
int i = 0;
|
||||
int ret;
|
||||
|
||||
mad_stream_buffer(&codec->stream, in, insize);
|
||||
ret = mad_frame_decode(&codec->frame, &codec->stream);
|
||||
if (ret < 0)
|
||||
{
|
||||
aerr("%s mp3 decode failed error %d\n", __func__, codec->stream.error);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mad_synth_frame(&codec->synth, &codec->frame);
|
||||
|
||||
nchannels = codec->synth.pcm.channels;
|
||||
nsamples = codec->synth.pcm.length;
|
||||
left_ch = codec->synth.pcm.samples[0];
|
||||
right_ch = codec->synth.pcm.samples[1];
|
||||
|
||||
ptr = codec->out;
|
||||
while (nsamples--)
|
||||
{
|
||||
int sample;
|
||||
|
||||
/* output sample(s) in 16-bit signed little-endian PCM */
|
||||
|
||||
sample = sim_audio_mp3_scale(*left_ch++);
|
||||
ptr[i] = (sample >> 0) & 0xff;
|
||||
ptr[i + 1] = (sample >> 8) & 0xff;
|
||||
|
||||
if (nchannels == 2)
|
||||
{
|
||||
sample = sim_audio_mp3_scale(*right_ch++);
|
||||
ptr[i + 2] = (sample >> 0) & 0xff;
|
||||
ptr[i + 3] = (sample >> 8) & 0xff;
|
||||
}
|
||||
|
||||
i += sizeof(short) * nchannels;
|
||||
}
|
||||
|
||||
*out = ptr;
|
||||
*outsize = codec->synth.pcm.length * nchannels * sizeof(short);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sim_audio_mp3_uninit(void *handle)
|
||||
{
|
||||
struct sim_decoder_mp3_s *codec = (struct sim_decoder_mp3_s *)handle;
|
||||
|
||||
mad_synth_finish(&(codec->synth));
|
||||
mad_frame_finish(&(codec->frame));
|
||||
mad_stream_finish(&(codec->stream));
|
||||
|
||||
kmm_free(codec->out);
|
||||
kmm_free(codec);
|
||||
}
|
||||
|
||||
static void *sim_audio_pcm_init(void)
|
||||
{
|
||||
return kmm_malloc(sizeof(struct sim_codec_pcm_s));
|
||||
}
|
||||
|
||||
static int sim_audio_pcm_process(void *handle,
|
||||
uint8_t *in, uint32_t insize,
|
||||
uint8_t **out, uint32_t *outsize)
|
||||
{
|
||||
*out = in;
|
||||
*outsize = insize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sim_audio_pcm_uninit(void *handle)
|
||||
{
|
||||
kmm_free(handle);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
Loading…
Reference in New Issue
Block a user