From 18bca596d498bc964ce854a7dd5509f348f11713 Mon Sep 17 00:00:00 2001 From: qiaohaijiao1 Date: Tue, 8 Nov 2022 11:08:49 +0800 Subject: [PATCH] sim/sim_alsa.c: add mp3 offload playback on sim use host libmad to simulate read DSP. Signed-off-by: qiaohaijiao1 --- arch/sim/src/Makefile | 1 + arch/sim/src/sim/sim_alsa.c | 303 +++++++++++++++++++++++++++++++++++- 2 files changed, 296 insertions(+), 8 deletions(-) diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 71e9a126b5..55694003c8 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -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) diff --git a/arch/sim/src/sim/sim_alsa.c b/arch/sim/src/sim/sim_alsa.c index c97d00d019..c4bf5018c5 100644 --- a/arch/sim/src/sim/sim_alsa.c +++ b/arch/sim/src/sim/sim_alsa.c @@ -31,17 +31,49 @@ #include #include +#include /**************************************************************************** * 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 ****************************************************************************/