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:
qiaohaijiao1 2022-11-08 11:08:49 +08:00 committed by Xiang Xiao
parent e6ec6e1a94
commit 18bca596d4
2 changed files with 296 additions and 8 deletions

View File

@ -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)

View File

@ -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
****************************************************************************/