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