sim/sim_alsa.c: support streaming data when offload playback.

Audio offload playback, change data organization from fragmented to
streaming.

Signed-off-by: qiaohaijiao1 <qiaohaijiao1@xiaomi.com>
This commit is contained in:
qiaohaijiao1 2023-01-13 15:12:38 +08:00 committed by Xiang Xiao
parent 26ac5335e5
commit 38039df16a
4 changed files with 590 additions and 235 deletions

View File

@ -201,6 +201,7 @@ endif
ifeq ($(CONFIG_SIM_SOUND_ALSA),y)
CSRCS += sim_alsa.c
CSRCS += sim_offload.c
STDLIBS += -lasound
STDLIBS += -lmad
endif

View File

@ -31,49 +31,19 @@
#include <debug.h>
#include <alsa/asoundlib.h>
#include <mad.h>
#include "sim_offload.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;
@ -96,20 +66,9 @@ struct sim_audio_s
snd_mixer_elem_t *volume;
void *codec;
const struct sim_codec_ops_s *ops;
};
const 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;
struct ap_buffer_s *aux;
};
/****************************************************************************
@ -142,19 +101,6 @@ 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
****************************************************************************/
@ -178,26 +124,6 @@ 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;
/****************************************************************************
@ -297,7 +223,7 @@ 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++)
for (i = 0; g_codec_ops[i].format != AUDIO_FMT_UNDEF; i++)
{
if (g_codec_ops[i].format == fmt &&
((priv->playback && g_codec_ops[i].flags & AUDCODEC_DEC) ||
@ -536,8 +462,21 @@ static int sim_audio_shutdown(struct audio_lowerhalf_s *dev)
static int sim_audio_start(struct audio_lowerhalf_s *dev)
{
struct sim_audio_s *priv = (struct sim_audio_s *)dev;
struct audio_buf_desc_s buf_desc;
int ret;
priv->codec = priv->ops->init();
/* reserved aux buffer. */
buf_desc.numbytes = priv->buffer_size * 2;
buf_desc.u.pbuffer = &priv->aux;
ret = apb_alloc(&buf_desc);
if (ret != sizeof(buf_desc))
{
return -ENOMEM;
}
priv->codec = priv->ops->init(NULL);
if (priv->codec == NULL)
{
return -ENOSYS;
@ -571,6 +510,9 @@ static int sim_audio_stop(struct audio_lowerhalf_s *dev)
priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
#endif
apb_free(priv->aux);
priv->aux = NULL;
priv->ops->uninit(priv->codec);
priv->ops = NULL;
@ -665,12 +607,85 @@ static int sim_audio_release(struct audio_lowerhalf_s *dev)
return 0;
}
static int sim_audio_process_playback(struct sim_audio_s *priv,
struct ap_buffer_s *apb,
bool *dequeue)
{
struct ap_buffer_s *aux = priv->aux;
uint8_t *out = NULL;
uint8_t *in = NULL;
uint32_t outsize;
uint32_t insize;
int ret;
/* 1, copy apb buffer to aux when the apb buffer just enqueued.
* */
if (apb->flags & AUDIO_APB_OUTPUT_ENQUEUED)
{
memcpy(aux->samp + aux->nbytes, apb->samp, apb->nbytes);
aux->nbytes += apb->nbytes;
apb->flags &= ~AUDIO_APB_OUTPUT_ENQUEUED;
}
in = aux->samp + aux->curbyte;
insize = aux->nbytes - aux->curbyte;
/* 2, decode or passthrough. */
ret = priv->ops->process(priv->codec, in, insize, &out, &outsize);
if (ret < 0)
{
/* 3, if return -ENODATA, means there is no enough data in aux apb.
* memmove the remaining data to aux->samp.
* */
if (ret == -ENODATA)
{
memmove(aux->samp, in, insize);
aux->curbyte = 0;
aux->nbytes = insize;
*dequeue = true;
}
else
{
/* 4, if other error, increase apb->curbyte and try again. */
aux->curbyte++;
}
ret = 0;
}
else
{
/* 5, decode success, process remain data. write to alsa. */
aux->curbyte += ret;
ret = snd_pcm_writei(priv->pcm, out, outsize / priv->frame_size);
ret *= priv->frame_size;
}
/* 6, whether send DEQUEUE msg to apps. */
if (aux->curbyte == aux->nbytes)
{
aux->curbyte = 0;
aux->nbytes = 0;
*dequeue = true;
}
/* 7, return actual bytes which write to alsa driver. */
return ret;
}
static void sim_audio_process(struct sim_audio_s *priv)
{
struct ap_buffer_s *apb;
snd_pcm_sframes_t expect;
struct ap_buffer_s *apb;
snd_pcm_sframes_t avail;
uint8_t *out = NULL;
bool dequeue = false;
uint32_t outsize;
int ret = 0;
@ -705,15 +720,7 @@ static void sim_audio_process(struct sim_audio_s *priv)
if (priv->playback)
{
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;
ret = sim_audio_process_playback(priv, apb, &dequeue);
}
else
{
@ -730,16 +737,17 @@ static void sim_audio_process(struct sim_audio_s *priv)
return;
}
ret = outsize;
apb->curbyte += ret;
dequeue = true;
}
if (ret >= 0)
if (dequeue)
{
bool final = false;
dq_remfirst(&priv->pendq);
apb->nbytes = ret;
apb->nbytes = apb->curbyte;
if (apb->flags & AUDIO_APB_FINAL)
{
final = true;
@ -853,147 +861,6 @@ 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
****************************************************************************/

View File

@ -0,0 +1,410 @@
/****************************************************************************
* arch/sim/src/sim/posix/sim_offload.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/kmalloc.h>
#include <debug.h>
#include <mad.h>
#include "sim_offload.h"
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static void *sim_audio_mp3_init(struct audio_info_s *info);
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(struct audio_info_s *info);
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);
/****************************************************************************
* Public Data
****************************************************************************/
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
},
{
AUDIO_FMT_UNDEF,
0,
NULL,
NULL,
NULL,
NULL
},
};
/****************************************************************************
* Private Types
****************************************************************************/
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;
};
/****************************************************************************
* Private Data
****************************************************************************/
static const uint16_t g_mpa_freq_tab[3] =
{
44100, 48000, 32000
};
static const uint16_t g_mpa_bitrate_tab[2][3][15] =
{
{
{
0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448
},
{
0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384
},
{
0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320
}
},
{
{
0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256
},
{
0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160
},
{
0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160
}
}
};
/****************************************************************************
* Private Functions
****************************************************************************/
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 int sim_audio_check_mpeg(uint32_t header)
{
/* header */
if ((header & 0xffe00000) != 0xffe00000)
{
return -EINVAL;
}
/* version check */
if ((header & (3 << 19)) == 1 << 19)
{
return -EINVAL;
}
/* layer check */
if ((header & (3 << 17)) == 0)
{
return -EINVAL;
}
/* bit rate */
if ((header & (0xf << 12)) == 0xf << 12)
{
return -EINVAL;
}
/* frequency */
if ((header & (3 << 10)) == 3 << 10)
{
return -EINVAL;
}
return 0;
}
static int sim_audio_mp3_check(uint32_t header)
{
int sample_rate;
int frame_size;
int padding;
int mpeg25;
int sr_idx;
int br_idx;
int layer;
int lsf;
int ret;
ret = sim_audio_check_mpeg(header);
if (ret < 0)
{
return ret;
}
if (header & (1 << 20))
{
lsf = (header & (1 << 19)) ? 0 : 1;
mpeg25 = 0;
}
else
{
lsf = 1;
mpeg25 = 1;
}
layer = 4 - ((header >> 17) & 3);
br_idx = (header >> 12) & 0xf;
sr_idx = (header >> 10) & 3;
padding = (header >> 9) & 1;
if (sr_idx >= sizeof(g_mpa_freq_tab) / sizeof(g_mpa_freq_tab[0]) ||
br_idx >= 0xf)
{
return -EINVAL;
}
sample_rate = g_mpa_freq_tab[sr_idx] >> (lsf + mpeg25);
if (br_idx != 0)
{
frame_size = g_mpa_bitrate_tab[lsf][layer - 1][br_idx];
switch (layer)
{
case 1:
frame_size = (frame_size * 12000) / sample_rate;
frame_size = (frame_size + padding) * 4;
break;
case 2:
frame_size = (frame_size * 144000) / sample_rate;
frame_size += padding;
break;
default:
case 3:
frame_size = (frame_size * 144000) / (sample_rate << lsf);
frame_size += padding;
break;
}
}
else
{
/* if no frame size computed, signal it */
return -EINVAL;
}
return frame_size;
}
static void *sim_audio_mp3_init(struct audio_info_s *info)
{
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 mpa_header;
int nchannels;
int nsamples;
uint8_t *ptr;
int i = 0;
int size;
int ret;
if (insize < 4)
{
return -ENODATA;
}
mpa_header = in[0] << 24 | in[1] << 16 | in[2] << 8 | in[3];
size = sim_audio_mp3_check(mpa_header);
if (size < 0)
{
return -EINVAL;
}
if (insize < size + 8)
{
return -ENODATA;
}
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 * sizeof(short) * nchannels;
return size;
}
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(struct audio_info_s *info)
{
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 *outsize;
}
static void sim_audio_pcm_uninit(void *handle)
{
kmm_free(handle);
}

View File

@ -0,0 +1,77 @@
/****************************************************************************
* arch/sim/src/sim/posix/sim_offload.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __ARCH_SIM_SRC_POSIX_SIM_OFFLOAD_H
#define __ARCH_SIM_SRC_POSIX_SIM_OFFLOAD_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/audio/audio.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define AUDCODEC_DEC 0x01
#define AUDCODEC_ENC 0x10
/****************************************************************************
* Public Type Definitions
****************************************************************************/
typedef struct sim_codec_ops_s
{
uint8_t format;
uint8_t flags;
/* init codec handle */
void *(*init)(struct audio_info_s *info);
/* 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] is pcm data with [outsize] when decode,
* [out] is compress data with [outsize] when encode.
* return: < 0 means failed. >= 0 means size of source data consumed.
*/
int (*process)(void *handle, uint8_t *in, uint32_t insize,
uint8_t **out, unsigned int *outsize);
/* uninit codec handle */
void (*uninit)(void *handle);
} sim_codec_ops_s;
/****************************************************************************
* Public Data
****************************************************************************/
extern const sim_codec_ops_s g_codec_ops[];
#endif /* __ARCH_SIM_SRC_POSIX_SIM_OFFLOAD_H */