You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/12/02 14:55:46 UTC
[nuttx] 02/02: sim/sim_alsa.c: add mp3 offload playback on sim
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 18bca596d498bc964ce854a7dd5509f348f11713
Author: qiaohaijiao1 <qi...@xiaomi.com>
AuthorDate: Tue Nov 8 11:08:49 2022 +0800
sim/sim_alsa.c: add mp3 offload playback on sim
use host libmad to simulate read DSP.
Signed-off-by: qiaohaijiao1 <qi...@xiaomi.com>
---
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 <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
****************************************************************************/