mirror of
https://github.com/apache/nuttx.git
synced 2026-05-28 03:45:50 +08:00
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:
@@ -201,6 +201,7 @@ endif
|
|||||||
ifeq ($(CONFIG_SIM_SOUND_ALSA),y)
|
ifeq ($(CONFIG_SIM_SOUND_ALSA),y)
|
||||||
CSRCS += sim_alsa.c
|
CSRCS += sim_alsa.c
|
||||||
STDLIBS += -lasound
|
STDLIBS += -lasound
|
||||||
|
STDLIBS += -lmad
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(CONFIG_SIM_HOSTFS),y)
|
ifeq ($(CONFIG_SIM_HOSTFS),y)
|
||||||
|
|||||||
+295
-8
@@ -31,17 +31,49 @@
|
|||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
|
#include <mad.h>
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Pre-processor Definitions
|
* Pre-processor Definitions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#define AUDMIN(a,b) ((a) > (b) ? (b) : (a))
|
#define AUDMIN(a,b) ((a) > (b) ? (b) : (a))
|
||||||
|
#define AUDCODEC_DEC 0x01
|
||||||
|
#define AUDCODEC_ENC 0x10
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Types
|
* 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 sim_audio_s
|
||||||
{
|
{
|
||||||
struct audio_lowerhalf_s dev;
|
struct audio_lowerhalf_s dev;
|
||||||
@@ -61,6 +93,22 @@ struct sim_audio_s
|
|||||||
snd_pcm_t *pcm;
|
snd_pcm_t *pcm;
|
||||||
snd_mixer_t *mixer;
|
snd_mixer_t *mixer;
|
||||||
snd_mixer_elem_t *volume;
|
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_reserve(struct audio_lowerhalf_s *dev);
|
||||||
static int sim_audio_release(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
|
* Private Data
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@@ -116,6 +177,26 @@ static const struct audio_ops_s g_sim_audio_ops =
|
|||||||
.release = sim_audio_release,
|
.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;
|
static sq_queue_t g_sim_audio;
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@@ -211,6 +292,27 @@ fail:
|
|||||||
return ret;
|
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)
|
static int sim_audio_open(struct sim_audio_s *priv)
|
||||||
{
|
{
|
||||||
snd_pcm_t *pcm;
|
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_INPUT) |
|
||||||
AUDIO_TYPE_FEATURE |
|
AUDIO_TYPE_FEATURE |
|
||||||
AUDIO_TYPE_PROCESSING;
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
|
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_OUTPUT:
|
||||||
case AUDIO_TYPE_INPUT:
|
case AUDIO_TYPE_INPUT:
|
||||||
|
|
||||||
caps->ac_channels = 2;
|
caps->ac_channels = 2;
|
||||||
|
|
||||||
switch (caps->ac_subtype)
|
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_OUTPUT:
|
||||||
case AUDIO_TYPE_INPUT:
|
case AUDIO_TYPE_INPUT:
|
||||||
|
|
||||||
priv->sample_rate = caps->ac_controls.hw[0] |
|
priv->sample_rate = caps->ac_controls.hw[0] |
|
||||||
(caps->ac_controls.b[3] << 16);
|
(caps->ac_controls.b[3] << 16);
|
||||||
priv->channels = caps->ac_channels;
|
priv->channels = caps->ac_channels;
|
||||||
priv->bps = caps->ac_controls.b[2];
|
priv->bps = caps->ac_controls.b[2];
|
||||||
priv->frame_size = priv->bps / 8 * priv->channels;
|
priv->frame_size = priv->bps / 8 * priv->channels;
|
||||||
|
|
||||||
|
sim_audio_config_ops(priv, caps->ac_subtype);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
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);
|
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);
|
priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
priv->ops->uninit(priv->codec);
|
||||||
|
priv->ops = NULL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -553,6 +668,8 @@ static void sim_audio_process(struct sim_audio_s *priv)
|
|||||||
struct ap_buffer_s *apb;
|
struct ap_buffer_s *apb;
|
||||||
snd_pcm_sframes_t expect;
|
snd_pcm_sframes_t expect;
|
||||||
snd_pcm_sframes_t avail;
|
snd_pcm_sframes_t avail;
|
||||||
|
uint8_t *out = NULL;
|
||||||
|
uint32_t outsize;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!priv->pcm)
|
if (!priv->pcm)
|
||||||
@@ -566,9 +683,17 @@ static void sim_audio_process(struct sim_audio_s *priv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect = priv->playback ? apb->nbytes / priv->frame_size
|
if (priv->ops->get_samples)
|
||||||
: AUDMIN(apb->nmaxbytes, priv->buffer_size)
|
{
|
||||||
/ priv->frame_size;
|
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);
|
avail = snd_pcm_avail(priv->pcm);
|
||||||
if (avail < expect)
|
if (avail < expect)
|
||||||
{
|
{
|
||||||
@@ -578,11 +703,32 @@ static void sim_audio_process(struct sim_audio_s *priv)
|
|||||||
|
|
||||||
if (priv->playback)
|
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
|
else
|
||||||
{
|
{
|
||||||
ret = snd_pcm_readi(priv->pcm, apb->samp, expect);
|
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)
|
if (ret >= 0)
|
||||||
@@ -591,7 +737,7 @@ static void sim_audio_process(struct sim_audio_s *priv)
|
|||||||
|
|
||||||
dq_remfirst(&priv->pendq);
|
dq_remfirst(&priv->pendq);
|
||||||
|
|
||||||
apb->nbytes = ret * priv->frame_size;
|
apb->nbytes = ret;
|
||||||
if (apb->flags & AUDIO_APB_FINAL)
|
if (apb->flags & AUDIO_APB_FINAL)
|
||||||
{
|
{
|
||||||
final = true;
|
final = true;
|
||||||
@@ -702,6 +848,147 @@ fail:
|
|||||||
return 0;
|
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
|
* Public Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|||||||
Reference in New Issue
Block a user