PCM: Major simplification of the sub-sampling design. Now need no extra buffers because sub-sampling is done in place
This commit is contained in:
parent
2efb594f50
commit
ab26d97d74
@ -124,18 +124,8 @@ struct pcm_decode_s
|
|||||||
/* Fast forward support */
|
/* Fast forward support */
|
||||||
|
|
||||||
uint8_t subsample; /* Fast forward rate: See AUDIO_SUBSAMPLE_* defns */
|
uint8_t subsample; /* Fast forward rate: See AUDIO_SUBSAMPLE_* defns */
|
||||||
uint8_t skip; /* Number of samples to be skipped */
|
uint8_t skip; /* Number of sample bytes to be skipped */
|
||||||
uint8_t npartial; /* Size of the partially copied sample */
|
uint8_t npartial; /* Size of the partially copied sample */
|
||||||
|
|
||||||
/* REVISIT: I think we could get rid of all of this special buffer
|
|
||||||
* processing. It seems like we should be able to do the sub-sampling
|
|
||||||
* in place without any additional buffers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct audio_buf_desc_s bufdesc; /* Allocated buffer description */
|
|
||||||
FAR struct ap_buffer_s *ffwd; /* Audio buffer being subsampled into */
|
|
||||||
FAR struct ap_buffer_s *next; /* Next available, empty audio buffer */
|
|
||||||
FAR struct ap_buffer_s *apb; /* Allocated audio buffer */
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -161,29 +151,11 @@ static uint16_t pcm_leuint32(uint32_t value);
|
|||||||
|
|
||||||
static inline bool pcm_validwav(FAR const struct wav_header_s *wav);
|
static inline bool pcm_validwav(FAR const struct wav_header_s *wav);
|
||||||
static bool pcm_parsewav(FAR struct pcm_decode_s *priv, uint8_t *data);
|
static bool pcm_parsewav(FAR struct pcm_decode_s *priv, uint8_t *data);
|
||||||
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
||||||
static void pcm_upper_callback(FAR struct pcm_decode_s *priv,
|
|
||||||
uint16_t reason, FAR struct ap_buffer_s *apb,
|
|
||||||
uint16_t status, FAR void *session);
|
|
||||||
#else
|
|
||||||
static void pcm_upper_callback(FAR struct pcm_decode_s *priv,
|
|
||||||
uint16_t reason, FAR struct ap_buffer_s *apb,
|
|
||||||
uint16_t status);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
|
#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
|
||||||
static int pcm_subsample_configure(FAR struct pcm_decode_s *priv,
|
static void pcm_subsample_configure(FAR struct pcm_decode_s *priv,
|
||||||
uint8_t subsample);
|
uint8_t subsample);
|
||||||
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
static void pcm_subsample(FAR struct pcm_decode_s *priv,
|
||||||
static void pcm_subsample_callback(FAR struct pcm_decode_s *priv,
|
|
||||||
uint16_t reason, FAR struct ap_buffer_s *apb,
|
|
||||||
uint16_t status, FAR void *session);
|
|
||||||
#else
|
|
||||||
static void pcm_subsample_callback(FAR struct pcm_decode_s *priv,
|
|
||||||
uint16_t reason, FAR struct ap_buffer_s *apb,
|
|
||||||
uint16_t status);
|
|
||||||
#endif
|
|
||||||
static FAR struct ap_buffer_s *pcm_subsample(FAR struct pcm_decode_s *priv,
|
|
||||||
FAR struct ap_buffer_s *apb);
|
FAR struct ap_buffer_s *apb);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -445,39 +417,6 @@ static bool pcm_parsewav(FAR struct pcm_decode_s *priv, uint8_t *data)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* Name: pcm_upper_callback
|
|
||||||
*
|
|
||||||
* Description:
|
|
||||||
* The lower level driver has just returned a buffer that belongs to the
|
|
||||||
* uppder level. We need to forward the buffer to the upper level for
|
|
||||||
* disposition.
|
|
||||||
*
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
||||||
static void pcm_upper_callback(FAR struct pcm_decode_s *priv,
|
|
||||||
uint16_t reason, FAR struct ap_buffer_s *apb,
|
|
||||||
uint16_t status, FAR void *session)
|
|
||||||
#else
|
|
||||||
static void pcm_upper_callback(FAR struct pcm_decode_s *priv,
|
|
||||||
uint16_t reason, FAR struct ap_buffer_s *apb,
|
|
||||||
uint16_t status)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
DEBUGASSERT(priv && apb && priv->export.upper);
|
|
||||||
|
|
||||||
/* The buffer belongs to to an upper level. Just forward the event to
|
|
||||||
* the next level up.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
||||||
priv->export.upper(priv->export.priv, reason, apb, status, session);
|
|
||||||
#else
|
|
||||||
priv->export.upper(priv->export.priv, reason, apb, status);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: pcm_subsample_configure
|
* Name: pcm_subsample_configure
|
||||||
*
|
*
|
||||||
@ -488,15 +427,9 @@ static void pcm_upper_callback(FAR struct pcm_decode_s *priv,
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
|
#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
|
||||||
static int pcm_subsample_configure(FAR struct pcm_decode_s *priv,
|
static void pcm_subsample_configure(FAR struct pcm_decode_s *priv,
|
||||||
uint8_t subsample)
|
uint8_t subsample)
|
||||||
{
|
{
|
||||||
FAR struct audio_lowerhalf_s *lower = priv->lower;
|
|
||||||
#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
|
|
||||||
struct ap_buffer_info_s bufinfo;
|
|
||||||
#endif
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
audvdbg("subsample: %d\n", subsample);
|
audvdbg("subsample: %d\n", subsample);
|
||||||
|
|
||||||
/* Three possibilities:
|
/* Three possibilities:
|
||||||
@ -507,84 +440,18 @@ static int pcm_subsample_configure(FAR struct pcm_decode_s *priv,
|
|||||||
|
|
||||||
if (priv->subsample == AUDIO_SUBSAMPLE_NONE)
|
if (priv->subsample == AUDIO_SUBSAMPLE_NONE)
|
||||||
{
|
{
|
||||||
/* There should be no audio buffer allocated in this case */
|
|
||||||
|
|
||||||
DEBUGASSERT(priv->ffwd == NULL);
|
|
||||||
|
|
||||||
/* Ignore request to stop fast forwarding if we are already
|
/* Ignore request to stop fast forwarding if we are already
|
||||||
* fast forwarding.
|
* fast forwarding.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (subsample != AUDIO_SUBSAMPLE_NONE)
|
if (subsample != AUDIO_SUBSAMPLE_NONE)
|
||||||
{
|
{
|
||||||
/* Check if we already have an audio buffer. This might happen in
|
audvdbg("Start sub-sampling\n");
|
||||||
* certain race conditions were we exit and return to fast forward
|
|
||||||
* mode while the audio buffer is in possession of the lower level
|
|
||||||
* driver.
|
|
||||||
*/
|
|
||||||
|
|
||||||
audvdbg("Start subsampling: apb=%p\n", priv->apb);
|
|
||||||
|
|
||||||
/* REVISIT: I think we could get rid of this special buffer. It
|
|
||||||
* seems like we should be able to do the sub-sampling in place
|
|
||||||
* without any additional buffers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (priv->apb == NULL)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
|
|
||||||
/* Get the lower level driver's preferred buffer properties */
|
|
||||||
|
|
||||||
DEBUGASSERT(lower->ioctl);
|
|
||||||
ret = lower->ioctl(lower, AUDIOIOC_GETBUFFERINFO,
|
|
||||||
(unsigned long)&bufinfo))
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
/* Driver doesn't report it's buffer size. Use defaults. */
|
|
||||||
|
|
||||||
bufinfo.buffer_size = CONFIG_AUDIO_BUFFER_NUMBYTES;
|
|
||||||
bufinfo.nbuffers = CONFIG_AUDIO_NUM_BUFFERS;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Configure the buffer descriptor */
|
|
||||||
|
|
||||||
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
||||||
priv->bufdesc.session = priv->session;
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
|
|
||||||
priv->bufdesc.numbytes = bufinfo.buffer_size;
|
|
||||||
#else
|
|
||||||
priv->bufdesc.numbytes = CONFIG_AUDIO_BUFFER_NUMBYTES;
|
|
||||||
#endif
|
|
||||||
priv->bufdesc.u.ppBuffer = &priv->apb;
|
|
||||||
|
|
||||||
/* Perform the allocation */
|
|
||||||
|
|
||||||
if (lower->ops->allocbuffer)
|
|
||||||
{
|
|
||||||
ret = lower->ops->allocbuffer(lower, &priv->bufdesc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret = apb_alloc(&priv->bufdesc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Did we get an audio buffer? */
|
|
||||||
|
|
||||||
if (ret < 0 || priv->apb == NULL)
|
|
||||||
{
|
|
||||||
auddbg("ERROR: Failed to allocate an audio buffer\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Save the current subsample setting. Subsampling will begin on
|
/* Save the current subsample setting. Subsampling will begin on
|
||||||
* then next audio buffer that we receive.
|
* then next audio buffer that we receive.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
priv->ffwd = priv->apb;
|
|
||||||
priv->next = NULL;
|
|
||||||
priv->npartial = 0;
|
priv->npartial = 0;
|
||||||
priv->skip = 0;
|
priv->skip = 0;
|
||||||
priv->subsample = subsample;
|
priv->subsample = subsample;
|
||||||
@ -592,15 +459,11 @@ static int pcm_subsample_configure(FAR struct pcm_decode_s *priv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 2. Were already fast forwarding and we have been asked to change the
|
/* 2. Were already fast forwarding and we have been asked to change the
|
||||||
* subsampling rate.
|
* sub-sampling rate.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
else if (subsample != AUDIO_SUBSAMPLE_NONE)
|
else if (subsample != AUDIO_SUBSAMPLE_NONE)
|
||||||
{
|
{
|
||||||
/* We should already have an audio buffer allocated in this case */
|
|
||||||
|
|
||||||
DEBUGASSERT(priv->apb != NULL && priv->ffwd != NULL);
|
|
||||||
|
|
||||||
/* Just save the current subsample setting. It will take effect
|
/* Just save the current subsample setting. It will take effect
|
||||||
* on the next audio buffer that we receive.
|
* on the next audio buffer that we receive.
|
||||||
*/
|
*/
|
||||||
@ -614,159 +477,16 @@ static int pcm_subsample_configure(FAR struct pcm_decode_s *priv,
|
|||||||
|
|
||||||
else if (subsample != AUDIO_SUBSAMPLE_NONE)
|
else if (subsample != AUDIO_SUBSAMPLE_NONE)
|
||||||
{
|
{
|
||||||
DEBUGASSERT(priv->apb != NULL && priv->ffwd != NULL);
|
audvdbg("Stop sub-sampling\n");
|
||||||
|
|
||||||
audvdbg("Stop subsampling\n");
|
/* Indicate that we are in normal play mode. This will take effect
|
||||||
|
* when the next audio buffer is received.
|
||||||
/* Are still holding on to buffers that belong the upper level? */
|
|
||||||
|
|
||||||
if (priv->ffwd && priv->ffwd != priv->apb)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
||||||
pcm_upper_callback(priv, AUDIO_CALLBACK_DEQUEUE, priv->ffwd, OK, priv->session);
|
|
||||||
#else
|
|
||||||
pcm_upper_callback(priv, AUDIO_CALLBACK_DEQUEUE, priv->ffwd, OK);
|
|
||||||
#endif
|
|
||||||
priv->ffwd = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We need to free the our working audio buffer. But we also need
|
|
||||||
* to be careful: The lower level audio driver may have it now.
|
|
||||||
*
|
|
||||||
* If the lower level driver has the audio buffer, then we will defer
|
|
||||||
* freeing it until it is returned to us.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (priv->ffwd)
|
|
||||||
{
|
|
||||||
/* We have it. We can free the audio buffer. NOTE: This audio
|
|
||||||
* buffer might not be empty, but we will discard it anyway.
|
|
||||||
* Playing will begin at normal speed at the next, full audio
|
|
||||||
* buffer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (lower->ops->freebuffer)
|
|
||||||
{
|
|
||||||
(void)lower->ops->freebuffer(lower, &priv->bufdesc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
apb_free(priv->apb);
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->apb = NULL;
|
|
||||||
priv->ffwd = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* And indicate that we are in normal play mode. This will take
|
|
||||||
* effect when the next audio buffer is received.
|
|
||||||
*/
|
|
||||||
|
|
||||||
priv->next = NULL;
|
|
||||||
priv->npartial = 0;
|
priv->npartial = 0;
|
||||||
priv->skip = 0;
|
priv->skip = 0;
|
||||||
priv->subsample = AUDIO_SUBSAMPLE_NONE;
|
priv->subsample = AUDIO_SUBSAMPLE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* Name: pcm_subsample_callback
|
|
||||||
*
|
|
||||||
* Description:
|
|
||||||
* Our working audio buffer has been returned from the lower level driver.
|
|
||||||
*
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
|
|
||||||
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
|
||||||
static void pcm_subsample_callback(FAR struct pcm_decode_s *priv,
|
|
||||||
uint16_t reason,
|
|
||||||
FAR struct ap_buffer_s *apb,
|
|
||||||
uint16_t status, FAR void *session)
|
|
||||||
#else
|
|
||||||
static void pcm_subsample_callback(FAR struct pcm_decode_s *priv,
|
|
||||||
uint16_t reason,
|
|
||||||
FAR struct ap_buffer_s *apb,
|
|
||||||
uint16_t status)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
FAR struct audio_lowerhalf_s *lower;
|
|
||||||
|
|
||||||
DEBUGASSERT(priv->apb);
|
|
||||||
|
|
||||||
audvdbg("apb=%p subsample=%d\n", apb, priv->subsample);
|
|
||||||
|
|
||||||
/* Mark the buffer as empty */
|
|
||||||
|
|
||||||
apb->nbytes = 0;
|
|
||||||
apb->curbyte = 0;
|
|
||||||
|
|
||||||
/* Possibilities:
|
|
||||||
* 1. We are no longer streaming and we just need to free the buffer.
|
|
||||||
* This case happens if we exited fast forward mode while the lower-
|
|
||||||
* level driver had possession of or working audio buffer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (priv->subsample == AUDIO_SUBSAMPLE_NONE)
|
|
||||||
{
|
|
||||||
/* Free the working buffer */
|
|
||||||
|
|
||||||
lower = priv->lower;
|
|
||||||
if (lower->ops->freebuffer)
|
|
||||||
{
|
|
||||||
(void)lower->ops->freebuffer(lower, &priv->bufdesc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
apb_free(apb);
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->apb = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We are still streaming. At any given time, our working audio may be
|
|
||||||
* (a) the current working buffer, (a) the next working buffer or it, or
|
|
||||||
* (c) in possession of the lower level driver. None of these are the
|
|
||||||
* case now since we just got our working audio buffer back from the
|
|
||||||
*
|
|
||||||
* 2. We have no fast forward working buffer. Use it buffer as our
|
|
||||||
* fast forward working buffer. This should never happen.
|
|
||||||
*/
|
|
||||||
|
|
||||||
else if (priv->ffwd == NULL)
|
|
||||||
{
|
|
||||||
/* Set the returned buffer as the current working buffer */
|
|
||||||
|
|
||||||
priv->ffwd = apb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 2. We already have a fast word working buffer, but no next working
|
|
||||||
* buffer. Save out working audio buffer as the new next buffer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Set the returned buffer as the next working buffer
|
|
||||||
*
|
|
||||||
* REVISIT: This extra buffer is only used on the first buffer. There
|
|
||||||
* is something wrong:
|
|
||||||
*
|
|
||||||
* 1. First buffer: Our private buffer is the working buffer. The
|
|
||||||
* first audio buffer we receive is compressed into the working
|
|
||||||
* buffer and the working buffer goes to the lower level. The
|
|
||||||
* first buffer becomes next working buffer.
|
|
||||||
* 2. Next buffers: Are compressed into the working buffer, sent
|
|
||||||
* to the lower level, and become the next working buffer, etc.
|
|
||||||
* 3. The private buffer eventually comes back here and is saved
|
|
||||||
* as the "next" buffer, but it is never used again.
|
|
||||||
*/
|
|
||||||
|
|
||||||
DEBUGASSERT(priv->next == NULL);
|
|
||||||
priv->next = apb;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -774,43 +494,42 @@ static void pcm_subsample_callback(FAR struct pcm_decode_s *priv,
|
|||||||
* Name: pcm_subsample
|
* Name: pcm_subsample
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Given a newly received audio buffer, perform subsampling into our local
|
* Given a newly received audio buffer, perform sub-sampling in-place in
|
||||||
* working audio buffer. Return the subsampled audio buffer to send to
|
* the audio buffer. Since the sub-sampled data will always be smaller
|
||||||
* the lower level driver.
|
* than the original buffer, no additional buffering should be necessary.
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
|
#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
|
||||||
static FAR struct ap_buffer_s *pcm_subsample(FAR struct pcm_decode_s *priv,
|
static void pcm_subsample(FAR struct pcm_decode_s *priv,
|
||||||
FAR struct ap_buffer_s *apb)
|
FAR struct ap_buffer_s *apb)
|
||||||
{
|
{
|
||||||
FAR struct ap_buffer_s *work;
|
|
||||||
FAR const uint8_t *src;
|
FAR const uint8_t *src;
|
||||||
FAR uint8_t *dest;
|
FAR uint8_t *dest;
|
||||||
unsigned int destsize;
|
unsigned int destsize;
|
||||||
unsigned int srcsize;
|
unsigned int srcsize;
|
||||||
unsigned int skipsize;
|
unsigned int skipsize;
|
||||||
|
unsigned int copysize;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
/* Are we subsampling? */
|
/* Are we sub-sampling? */
|
||||||
|
|
||||||
if (priv->subsample == AUDIO_SUBSAMPLE_NONE)
|
if (priv->subsample == AUDIO_SUBSAMPLE_NONE)
|
||||||
{
|
{
|
||||||
/* No.. do nothing to the buffer */
|
/* No.. do nothing to the buffer */
|
||||||
|
|
||||||
return apb;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Yes.. then make sure that we have the required buffers in place */
|
/* Yes.. we will need to subsample the newly received buffer in-place by
|
||||||
|
* copying from the upper end of the buffer to the lower end.
|
||||||
|
*/
|
||||||
|
|
||||||
DEBUGASSERT(priv->ffwd && priv->apb);
|
src = &apb->samp[apb->curbyte];
|
||||||
|
dest = apb->samp;
|
||||||
|
|
||||||
/* Yes.. we will need to subsample the newly received buffer. */
|
srcsize = apb->nbytes - apb->curbyte;
|
||||||
|
destsize = apb->nmaxbytes;
|
||||||
work = priv->ffwd;
|
|
||||||
dest = &work->samp[work->nbytes];
|
|
||||||
|
|
||||||
DEBUGASSERT(work->nmaxbytes >= priv->align);
|
|
||||||
|
|
||||||
/* This is the number of bytes that we need to skip between samples */
|
/* This is the number of bytes that we need to skip between samples */
|
||||||
|
|
||||||
@ -820,10 +539,6 @@ static FAR struct ap_buffer_s *pcm_subsample(FAR struct pcm_decode_s *priv,
|
|||||||
|
|
||||||
if (priv->npartial > 0)
|
if (priv->npartial > 0)
|
||||||
{
|
{
|
||||||
/* How much data is available in the new audio buffer */
|
|
||||||
|
|
||||||
srcsize = apb->nbytes - apb->curbyte;
|
|
||||||
|
|
||||||
/* Let's get an impossible corner case out of the way. What if we
|
/* Let's get an impossible corner case out of the way. What if we
|
||||||
* received a tiny audio buffer. So small, that it (plus any previous
|
* received a tiny audio buffer. So small, that it (plus any previous
|
||||||
* sample) is smaller than one sample.
|
* sample) is smaller than one sample.
|
||||||
@ -831,45 +546,46 @@ static FAR struct ap_buffer_s *pcm_subsample(FAR struct pcm_decode_s *priv,
|
|||||||
|
|
||||||
if (priv->npartial + srcsize < priv->align)
|
if (priv->npartial + srcsize < priv->align)
|
||||||
{
|
{
|
||||||
/* Update the partial sample size */
|
/* Update the partial sample size and return the unmodified
|
||||||
|
* buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
priv->npartial += srcsize;
|
priv->npartial += srcsize;
|
||||||
|
return;
|
||||||
/* And just return the buffer with no modification */
|
|
||||||
|
|
||||||
return apb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* No, we do at least have enough to complete the sample. Copy copy data
|
/* We do at least have enough to complete the sample. If this data
|
||||||
* from the new audio buffer to complete the sample.
|
* does not resides at the correct position at the from of the audio
|
||||||
|
* buffer, then we will need to copy it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
src = &apb->samp[apb->curbyte];
|
copysize = priv->align - priv->npartial;
|
||||||
for (i = priv->npartial; i < priv->align; i++)
|
if (apb->curbyte > 0)
|
||||||
|
{
|
||||||
|
/* We have to copy down */
|
||||||
|
|
||||||
|
for (i = 0; i < copysize; i++)
|
||||||
{
|
{
|
||||||
*dest++ = *src++;
|
*dest++ = *src++;
|
||||||
apb->curbyte++;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->npartial = 0;
|
/* Update the number of bytes in the working buffer and reset the
|
||||||
|
* skip value
|
||||||
/* Update the number of bytes in the working buffer and reset the skip
|
|
||||||
* value
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
work->nbytes += skipsize;
|
priv->npartial = 0;
|
||||||
|
srcsize -= copysize;
|
||||||
|
destsize -= copysize;
|
||||||
priv->skip = skipsize;
|
priv->skip = skipsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now loop until either (1) the working audio buffer is full, or (2)
|
/* Now loop until either the entire audio buffer has been sub-sampling.
|
||||||
* the newly received audio buffer is empty.
|
* This copy in place works because we know that the sub-sampled data
|
||||||
|
* will always be smaller than the original data.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
src = &apb->samp[apb->curbyte];
|
while (srcsize > 0)
|
||||||
srcsize = apb->nbytes - apb->curbyte;
|
|
||||||
destsize = work->nmaxbytes - work->nbytes;
|
|
||||||
|
|
||||||
while (srcsize > 0 && destsize > 0)
|
|
||||||
{
|
{
|
||||||
/* Do we need to skip ahead in the buffer? */
|
/* Do we need to skip ahead in the buffer? */
|
||||||
|
|
||||||
@ -880,109 +596,17 @@ static FAR struct ap_buffer_s *pcm_subsample(FAR struct pcm_decode_s *priv,
|
|||||||
* number of bytes available in the newly received audio buffer.
|
* number of bytes available in the newly received audio buffer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
unsigned int discard = MIN(priv->skip, srcsize);
|
copysize = MIN(priv->skip, srcsize);
|
||||||
|
|
||||||
priv->skip -= discard;
|
priv->skip -= copysize;
|
||||||
apb->curbyte += discard;
|
src += copysize;
|
||||||
srcsize -= discard;
|
srcsize -= copysize;
|
||||||
|
|
||||||
/* Did we skip to the end of the audio buffer? */
|
|
||||||
|
|
||||||
if (srcsize <= 0)
|
|
||||||
{
|
|
||||||
goto exit_apb_empty;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the sample from the audio buffer into the working buffer. */
|
/* Copy the sample from the audio buffer into the working buffer. */
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unsigned int copysize;
|
|
||||||
|
|
||||||
/* Do we have space for the whole sample? */
|
|
||||||
|
|
||||||
if (srcsize < priv->align)
|
|
||||||
{
|
|
||||||
/* No.. this is a partial copy */
|
|
||||||
|
|
||||||
copysize = srcsize;
|
|
||||||
priv->npartial = srcsize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
copysize = priv->align;
|
|
||||||
priv->skip = skipsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now copy the sample from new audio buffer to working buffer */
|
|
||||||
|
|
||||||
for (i = 0; i < copysize; i++)
|
|
||||||
{
|
|
||||||
*dest++ = *src++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update indices and sizes and reset the skip value */
|
|
||||||
|
|
||||||
apb->curbyte += copysize;
|
|
||||||
srcsize -= copysize;
|
|
||||||
|
|
||||||
work->nbytes += copysize;
|
|
||||||
destsize -= copysize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We get here in one of two cases. Either (1) the source audio buffer
|
|
||||||
* became empty, and/or (2) the working audio buffer became which. Which
|
|
||||||
* is the case?
|
|
||||||
*
|
|
||||||
* REVISIT: Should there be a threshold here so that we do not send
|
|
||||||
* really tiny buffers to the lower-level driver.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (srcsize > 0)
|
|
||||||
{
|
|
||||||
/* How could the working buffer be empty in this case? */
|
|
||||||
|
|
||||||
DEBUGASSERT(work->nbytes > 0);
|
|
||||||
|
|
||||||
/* This seems overly complex. We either need another buffer or else
|
|
||||||
* we have to do this: Sub-ample in place.
|
|
||||||
*/
|
|
||||||
|
|
||||||
dest = apb->samp;
|
|
||||||
destsize = apb->nmaxbytes;
|
|
||||||
|
|
||||||
while (srcsize > 0)
|
|
||||||
{
|
|
||||||
/* Do we need to skip ahead in the buffer? */
|
|
||||||
|
|
||||||
if (priv->skip > 0)
|
|
||||||
{
|
|
||||||
/* How much can we skip in this buffer? Depends on the
|
|
||||||
* smaller of (1) the number of bytes that we need to skip
|
|
||||||
* and (2) the number of bytes available in the audio buffer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
unsigned int discard = MIN(priv->skip, srcsize);
|
|
||||||
|
|
||||||
priv->skip -= discard;
|
|
||||||
srcsize -= discard;
|
|
||||||
|
|
||||||
/* Did we skip to the end of the audio buffer? */
|
|
||||||
|
|
||||||
if (srcsize <= 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy the sample from the end of the audio buffer to the beginning. */
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unsigned int copysize;
|
|
||||||
|
|
||||||
/* Do we have space for the whole sample? */
|
/* Do we have space for the whole sample? */
|
||||||
|
|
||||||
if (srcsize < priv->align)
|
if (srcsize < priv->align)
|
||||||
@ -1000,7 +624,7 @@ static FAR struct ap_buffer_s *pcm_subsample(FAR struct pcm_decode_s *priv,
|
|||||||
priv->skip = skipsize;
|
priv->skip = skipsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now copy the sample from new audio buffer to working buffer */
|
/* Now copy the sample from the end of audio buffer to the beginning. */
|
||||||
|
|
||||||
for (i = 0; i < copysize; i++)
|
for (i = 0; i < copysize; i++)
|
||||||
{
|
{
|
||||||
@ -1008,7 +632,7 @@ static FAR struct ap_buffer_s *pcm_subsample(FAR struct pcm_decode_s *priv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Updates bytes available in the source buffer and bytes
|
/* Updates bytes available in the source buffer and bytes
|
||||||
* remaining in the dest buffer.
|
* remaining in the destination buffer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
srcsize -= copysize;
|
srcsize -= copysize;
|
||||||
@ -1016,44 +640,10 @@ static FAR struct ap_buffer_s *pcm_subsample(FAR struct pcm_decode_s *priv,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the buffer that we just emptied as the new working buffer */
|
/* Update the size and offset data in the audio buffer */
|
||||||
|
|
||||||
apb->curbyte = 0;
|
apb->curbyte = 0;
|
||||||
apb->nbytes = apb->nmaxbytes - destsize;
|
apb->nbytes = apb->nmaxbytes - destsize;
|
||||||
priv->ffwd = apb;
|
|
||||||
|
|
||||||
/* Return the working buffer */
|
|
||||||
|
|
||||||
return work;
|
|
||||||
}
|
|
||||||
|
|
||||||
exit_apb_empty:
|
|
||||||
/* We get here in the case that the source audio buffer is empty. The
|
|
||||||
* working buffer may be empty or non-empty.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Is there anything in the working buffer?
|
|
||||||
*
|
|
||||||
* REVISIT: Should there be a threshold here so that we do not send
|
|
||||||
* really tiny buffers to the lower-level driver.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (work->nbytes > 0)
|
|
||||||
{
|
|
||||||
/* Set the buffer that we just emptied as the new working buffer */
|
|
||||||
|
|
||||||
apb->curbyte = 0;
|
|
||||||
apb->nbytes = 0;
|
|
||||||
priv->ffwd = apb;
|
|
||||||
|
|
||||||
/* Return the working buffer */
|
|
||||||
|
|
||||||
return work;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We don't have any data to forward to the lower level driver */
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1129,9 +719,6 @@ static int pcm_configure(FAR struct audio_lowerhalf_s *dev,
|
|||||||
{
|
{
|
||||||
FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
|
FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)dev;
|
||||||
FAR struct audio_lowerhalf_s *lower;
|
FAR struct audio_lowerhalf_s *lower;
|
||||||
#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
|
|
||||||
int ret;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DEBUGASSERT(priv);
|
DEBUGASSERT(priv);
|
||||||
|
|
||||||
@ -1144,19 +731,13 @@ static int pcm_configure(FAR struct audio_lowerhalf_s *dev,
|
|||||||
if (caps->ac_type == AUDIO_TYPE_PROCESSING &&
|
if (caps->ac_type == AUDIO_TYPE_PROCESSING &&
|
||||||
caps->ac_format.hw == AUDIO_PU_SUBSAMPLE_FORWARD)
|
caps->ac_format.hw == AUDIO_PU_SUBSAMPLE_FORWARD)
|
||||||
{
|
{
|
||||||
/* Configure subsampling */
|
/* Configure sub-sampling and return to avoid forwarding the
|
||||||
|
* configuration to the lower level
|
||||||
ret = pcm_subsample_configure(priv, caps->ac_controls.b[0]);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
auddbg("ERROR: pcm_subsample_configure failed: %d\n", ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return to avoid forwarding the configuration to the lower level
|
|
||||||
* driver.
|
* driver.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return ret;
|
pcm_subsample_configure(priv, caps->ac_controls.b[0]);
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1440,12 +1021,11 @@ static int pcm_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
|
|||||||
audvdbg("Received: apb=%p curbyte=%d nbytes=%d\n",
|
audvdbg("Received: apb=%p curbyte=%d nbytes=%d\n",
|
||||||
apb, apb->curbyte, apb->nbytes);
|
apb, apb->curbyte, apb->nbytes);
|
||||||
|
|
||||||
/* Perform any necessary subsampling operations */
|
/* Perform any necessary sub-sampling operations */
|
||||||
|
|
||||||
apb = pcm_subsample(priv, apb);
|
pcm_subsample(priv, apb);
|
||||||
if (apb)
|
|
||||||
#endif
|
#endif
|
||||||
{
|
|
||||||
/* Then give the audio buffer to the lower driver */
|
/* Then give the audio buffer to the lower driver */
|
||||||
|
|
||||||
audvdbg("Pass to lower enqueuebuffer: apb=%p curbyte=%d nbytes=%d\n",
|
audvdbg("Pass to lower enqueuebuffer: apb=%p curbyte=%d nbytes=%d\n",
|
||||||
@ -1454,9 +1034,6 @@ static int pcm_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
|
|||||||
return lower->ops->enqueuebuffer(lower, apb);
|
return lower->ops->enqueuebuffer(lower, apb);
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No.. then this must be the first buffer that we have seen (since we
|
/* No.. then this must be the first buffer that we have seen (since we
|
||||||
* will error out out if the first buffer is smaller than the WAV file
|
* will error out out if the first buffer is smaller than the WAV file
|
||||||
* header. There is no attempt to reconstruct the full header from
|
* header. There is no attempt to reconstruct the full header from
|
||||||
@ -1511,12 +1088,11 @@ static int pcm_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
|
|||||||
audvdbg("Begin streaming: apb=%p curbyte=%d nbytes=%d\n",
|
audvdbg("Begin streaming: apb=%p curbyte=%d nbytes=%d\n",
|
||||||
apb, apb->curbyte, apb->nbytes);
|
apb, apb->curbyte, apb->nbytes);
|
||||||
|
|
||||||
/* Perform any necessary subsampling operations */
|
/* Perform any necessary sub-sampling operations */
|
||||||
|
|
||||||
apb = pcm_subsample(priv, apb);
|
pcm_subsample(priv, apb);
|
||||||
if (apb)
|
|
||||||
#endif
|
#endif
|
||||||
{
|
|
||||||
/* Then give the audio buffer to the lower driver */
|
/* Then give the audio buffer to the lower driver */
|
||||||
|
|
||||||
audvdbg("Pass to lower enqueuebuffer: apb=%p curbyte=%d nbytes=%d\n",
|
audvdbg("Pass to lower enqueuebuffer: apb=%p curbyte=%d nbytes=%d\n",
|
||||||
@ -1524,9 +1100,6 @@ static int pcm_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
|
|||||||
|
|
||||||
return lower->ops->enqueuebuffer(lower, apb);
|
return lower->ops->enqueuebuffer(lower, apb);
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is not a WAV file! */
|
/* This is not a WAV file! */
|
||||||
@ -1690,62 +1263,30 @@ static int pcm_release(FAR struct audio_lowerhalf_s *dev)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
/* Audio callback */
|
|
||||||
|
|
||||||
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
||||||
|
|
||||||
static void pcm_callback(FAR void *arg, uint16_t reason,
|
static void pcm_callback(FAR void *arg, uint16_t reason,
|
||||||
FAR struct ap_buffer_s *apb, uint16_t status,
|
FAR struct ap_buffer_s *apb, uint16_t status,
|
||||||
FAR void *session)
|
FAR void *session)
|
||||||
{
|
#else
|
||||||
FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)arg;
|
|
||||||
|
|
||||||
#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
|
|
||||||
/* If this is our internal working audio buffer that we use for subsampling,
|
|
||||||
* then we are the owner of the buffer and we don't want to return it to
|
|
||||||
* the upper level.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (priv->apb == apb)
|
|
||||||
{
|
|
||||||
pcm_subsample_callback(priv, reason, apb, status, session);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
/* Return the buffer to the upper level */
|
|
||||||
|
|
||||||
pcm_upper_callback(priv, reason, apb, status, session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* CONFIG_AUDIO_MULTI_SESSION */
|
|
||||||
|
|
||||||
static void pcm_callback(FAR void *arg, uint16_t reason,
|
static void pcm_callback(FAR void *arg, uint16_t reason,
|
||||||
FAR struct ap_buffer_s *apb, uint16_t status)
|
FAR struct ap_buffer_s *apb, uint16_t status)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)arg;
|
FAR struct pcm_decode_s *priv = (FAR struct pcm_decode_s *)arg;
|
||||||
|
|
||||||
#ifndef CONFIG_AUDIO_EXCLUDE_FFORWARD
|
|
||||||
/* If this is our internal working audio buffer that we use for sub-sampling,
|
DEBUGASSERT(priv && apb && priv->export.upper);
|
||||||
* then we are the owner of the buffer and we don't want to return it to
|
|
||||||
* the upper level.
|
/* The buffer belongs to to an upper level. Just forward the event to
|
||||||
|
* the next level up.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (priv->apb == apb)
|
#ifdef CONFIG_AUDIO_MULTI_SESSION
|
||||||
{
|
priv->export.upper(priv->export.priv, reason, apb, status, session);
|
||||||
pcm_subsample_callback(priv, reason, apb, status);
|
#else
|
||||||
}
|
priv->export.upper(priv->export.priv, reason, apb, status);
|
||||||
else
|
|
||||||
#endif
|
#endif
|
||||||
{
|
|
||||||
/* Return the buffer to the upper level */
|
|
||||||
|
|
||||||
pcm_upper_callback(priv, reason, apb, status);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CONFIG_AUDIO_MULTI_SESSION */
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Public Functions
|
* Public Functions
|
||||||
|
Loading…
Reference in New Issue
Block a user