Table of Contents | ||||||
---|---|---|---|---|---|---|
|
The Audio Processing module manages playback and record streams using two internal tasks:
- Playback task
- Record task
These two tasks are the glue between the µC/USB-Device Core and the Audio Peripheral Driver.
From a host perspective, a stream lifetime will always consist in:
- Opening a stream,
- Communicating on this stream,
- Closing a stream.
Sections below describe in more detailed manner the streams data flow.
Playback Stream
A playback stream carries audio data over an isochronous OUT endpoint. There is a one-to-one relation between an isochronous OUT endpoint, an AudioStreaming interface and a Terminal. presents the audio data flow implemented inside the Audio Processing module. The playback path relies on a ring buffer queue to synchronize the playback task, the core task and the codec ISR.
The playback task supports multi-streams. If the audio function uses several USB OUT Terminal types, each USB OUT Terminal is associated to one AudioStreaming interface structure that the playback task manipulates and updates during stream communication.
Warning | ||
---|---|---|
| ||
In case the ring buffer queue is empty when the playback task is submitting a buffer to the audio peripheral driver, a retry mechanism is used to re-submit the buffer 1 ms later. This delay allows other tasks to execute and a new buffer will become available in the ring buffer queue. The function |
Record Stream
The record task supports multi-streams . If the audio function uses several USB IN Terminal types, each USB IN Terminal is associated to one AudioStreaming interface structure posted in the record task's queue. Thus the record task can handle buffers from different streams.
The record data path takes care of the data rate adjustment. This is required for certain sampling frequencies that do not produce an integer number of audio samples per ms. Partial audio samples are not possible. For those sampling frequencies, the gives the required adjustment. The data rate adjustment is implemented in the isochronous IN transfer completion callback USBD_Audio_RecordIsocCmpl()
.
For instance, considering a sampling frequency of 44.1 kHz and a mono microphone, the audio class will send to the host isochronous transfers with a size of 44 samples each frame. In order to have 44 100 samples every second, the audio class will send 45 samples every 10 frames (that is every 10 ms). At one second, the host will have received 100 additional samples added to the 44 000 samples received with the 44-byte isochronous transfers.
Stream Correction
Playback Built-In Stream Correction
The built-in playback stream correction is active only when the constant USBD_AUDIO_CFG_PLAYBACK_CORR_EN
is set to DEF_ENABLED
. As explained in section Playback Stream, the stream correction is evaluated before the playback task provides a ready buffer to the audio peripheral driver. The evaluation relies on monitoring the playback ring buffer queue. Two thresholds are defined: a lower limit and an upper limit as shown in . The figure shows the four indexes used in the ring buffer queue. A buffer difference is computed between the indexes ProducerEnd
and ConsumerEnd
. For the playback path, ProducerEnd
is linked to the USB transfer completion while ConsumerEnd
is linked to the audio transfer completion. The buffer difference represent a circular distance between two indexes. If the distance is less than the lower limit, you have an underrun situation, that is the USB side does not produce fast enough the audio samples consumed by the codec. Conversely, if the distance is greater than the upper limit, this is an overrun situation, that is the USB side produces faster then the the codec can consume audio data. To keep the codec and USB in sync, a simple algorithm is used to add an audio sample in case of underrun and to remove a sample frame in case of overrun.
The frequency at which the playback stream correction is evaluated is configurable via the field CorrPeriodMs
of the structure USBD_AUDIO_STREAM_CFG
.
illustrates the algorithm to add an audio sample in case of underrun situation.
The frequency at which the playback stream correction is evaluated is configurable via the field CorrPeriodMs
of the structure USBD_AUDIO_STREAM_CFG
.
Info |
---|
The stream correction supports signed PCM and unsigned PCM8 format. |
Warning |
---|
This stream correction is convenient for low-cost audio design. It will give good results as long as the incoming USB audio sampling frequency is very close to the DAC input clock frequency. However, if the difference between the two frequencies is important, this will add audio distortion. |
illustrates the algorithm to remove an audio sample in case of overrun situation.
The playback stream correction offers the possibility to apply your own correction algorithm. If an underrun or overrun situation is detected, an application callback is called. shows an example of playback correction callback prototype and definition provided by the application.
Warning |
---|
If |
Record Built-In Stream Correction
There is also a built-in record stream correction active only when the constant USBD_AUDIO_CFG_RECORD_CORR_EN
is set to DEF_ENABLED
. As explained in the section Record Stream, when an isochronous IN transfer completes by calling the callback function USBD_Audio_RecordIsocCmpl()
, the stream correction is evaluated. The evaluation relies on monitoring the record ring buffer queue. Two thresholds are defined: a lower limit and an upper limit based on the same principle as shown in . For the record path, ProducerEnd
is linked to the audio transfer completion while ConsumerEnd
is linked to the USB transfer completion. This is the opposite of the playback. Moreover, the ring buffer queue scheme is common to the playback and record streams. And within the audio class, the definition of overrun and underrun situation is "USB-centric".
Consequently, if the lower limit is reached, you have an overrun situation, that is the USB side consumes a little bit faster than the the codec can produce. Conversely, the upper limit corresponds to an underrun situation, that is the USB side does not consume fast enough the audio samples produced by the codec. As opposed to the playback stream correction, no software algorithm is needed to add or remove an audio sample. The audio class will adjust the audio peripheral hardware by using the number of required record data bytes indicated by USBD_Audio_RecordBufGet()
. The correction is done implicitly by the audio peripheral hardware by directly getting the right number of audio samples (-1 sample frame or +1 sample frame) to accommodate the overrun or underrun situation.
The frequency at which the record stream correction is evaluated is configurable via the field CorrPeriodMs
of the structure USBD_AUDIO_STREAM_CFG
.
Playback Feedback Correction
The feedback correction (refer to section Feedback Endpoint for an overview of feedback) takes place when the configuration constant USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN
is set to DEF_ENABLED
and the AudioStreaming interface uses an isochronous OUT endpoint with asynchronous synchronization. As explained in section Playback Stream, the stream correction is evaluated in the function USBD_Audio_PlaybackCorrSynch()
before the playback task provides a ready buffer to the audio peripheral driver.
...
Table of Contents | ||||||
---|---|---|---|---|---|---|
|
The Audio Processing module manages playback and record streams using two internal tasks:
- Playback task
- Record task
These two tasks are the glue between the µC/USB-Device Core and the Audio Peripheral Driver.
From a host perspective, a stream lifetime will always consist in:
- Opening a stream,
- Communicating on this stream,
- Closing a stream.
Sections below describe in more detailed manner the streams data flow.
Playback Stream
A playback stream carries audio data over an isochronous OUT endpoint. There is a one-to-one relation between an isochronous OUT endpoint, an AudioStreaming interface and a Terminal. 101482827 presents the audio data flow implemented inside the Audio Processing module. The playback path relies on a ring buffer queue to synchronize the playback task, the core task and the codec ISR.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||
(1) The host activates the AudioStreaming interface #X by selecting the operational interface (request SET_INTERFACE sent for alternate setting 1). This marks the opening of the playback. The core task will call the function (2) The host then sets the sampling frequency for a certain isochronous OUT endpoint by sending a SET_CUR request. The function (3) The USB Device Controller fills the buffer with the isochronous audio data that have been sent by the host. The buffer is retrieved by the core task. As soon as one isochronous transfer is completed, the core task will call the callback
(4) The received buffer is then added to the ring buffer queue. (5) The core task will submit all the buffers it can to the USB device driver to feed the stream communication by calling (5a) Once a certain number of buffers (pre-buffering threshold) have been accumulated , the playback stream is started on the codec side by calling the function (6) Signalling the playback task consists in posting an AudioStreaming (AS) interface handle in a queue. The playback task wakes up and processes the handle. It submits a ready buffer taken from the ring buffer queue to the audio peripheral driver by calling the function
(7) In the same way as the core task in
(8) The buffer contains a chunk (1 ms of audio data) of audio stream. This audio chunk is encoded following a certain format. The audio peripheral driver might have to decode the audio chunk in order to correctly present the audio samples to the codec. (9) Each time a playback buffer is consumed by the codec, the audio peripheral driver ISR signals to the playback task the end of an audio transfer by calling the function (10) Afterwards, steps 3 to 9 are repeated over and over again until the host stops the playback by selecting the default AudioStreaming Interface (request SET_INTERFACE sent for alternate setting 0). At this time, the Audio Processing will stop the streaming on the codec side by calling the audio peripheral driver function
|
The playback task supports multi-streams. If the audio function uses several USB OUT Terminal types, each USB OUT Terminal is associated to one AudioStreaming interface structure that the playback task manipulates and updates during stream communication.
Warning | ||
---|---|---|
| ||
In case the ring buffer queue is empty when the playback task is submitting a buffer to the audio peripheral driver, a retry mechanism is used to re-submit the buffer 1 ms later. This delay allows other tasks to execute and a new buffer will become available in the ring buffer queue. The function |
Record Stream
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | |||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| |||||||||||||||||
(1) The host activates the AudioStreaming interface #X by selecting the operational interface (request SET_INTERFACE sent for alternate setting 1). The host then sets the sampling frequency for a certain isochronous IN endpoint by sending SET_CUR request. The function (2) When processing the SET_CUR(sampling frequency) request, the audio class will also start the record stream on the codec side by calling
(3) Once the first DMA transfer has completed, the audio peripheral driver will obtain the next receive buffer from the ring buffer queue by calling (4) The buffer will be filled with audio samples given by one or more ADCs (one ADC per logical channel). The buffer will contain 1 ms worth of audio samples. This 1 ms of audio samples should be encoded, either directly by the codec (hardware) or by the audio peripheral driver (software). Most of the time, the codec will provide the chunk of audio stream already encoded. The driver signals the end of an audio transfer to the record task by calling the function (5) The record task wakes up and retrieves the ready buffer from the audio peripheral driver by calling the audio peripheral driver function (6) To prime the audio stream, the record task waits for a certain number of buffers to be ready. The pre-bufferring threshold is always equal to ( There is a special situation where the record task can submit a new transfer. When the stream communication loop is broken, that is there are no more ongoing isochronous transfers in the USB device driver, the record task restarts the stream with a new USB transfer. (7) The USB device driver will send isochronous audio data to the host during a specific frame. (8) Upon completion of the isochronous IN transfer, the core task will call the callback
(9) The core task submits all the ready buffers it can to the USB device driver by calling
(10) Once the audio stream is initiated, the steps 3 to 8 will repeat over and over again until the host stops recording by selecting the default AudioStreaming Interface (request SET_INTERFACE sent for alternate setting 0). At this time, the Audio Processing will stop the streaming on the codec side by calling the audio peripheral driver function
|
The record task supports multi-streams . If the audio function uses several USB IN Terminal types, each USB IN Terminal is associated to one AudioStreaming interface structure posted in the record task's queue. Thus the record task can handle buffers from different streams.
The record data path takes care of the data rate adjustment. This is required for certain sampling frequencies that do not produce an integer number of audio samples per ms. Partial audio samples are not possible. For those sampling frequencies, the Table - Data Rate Adjustment gives the required adjustment. The data rate adjustment is implemented in the isochronous IN transfer completion callback USBD_Audio_RecordIsocCmpl()
.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||
|
For instance, considering a sampling frequency of 44.1 kHz and a mono microphone, the audio class will send to the host isochronous transfers with a size of 44 samples each frame. In order to have 44 100 samples every second, the audio class will send 45 samples every 10 frames (that is every 10 ms). At one second, the host will have received 100 additional samples added to the 44 000 samples received with the 44-byte isochronous transfers.
Stream Correction
Playback Built-In Stream Correction
The built-in playback stream correction is active only when the constant USBD_AUDIO_CFG_PLAYBACK_CORR_EN
is set to DEF_ENABLED
. As explained in section Playback Stream, the stream correction is evaluated before the playback task provides a ready buffer to the audio peripheral driver. The evaluation relies on monitoring the playback ring buffer queue. Two thresholds are defined: a lower limit and an upper limit as shown in Figure - Playback Ring Buffers Queue Monitoring. The figure shows the four indexes used in the ring buffer queue. A buffer difference is computed between the indexes ProducerEnd
and ConsumerEnd
. For the playback path, ProducerEnd
is linked to the USB transfer completion while ConsumerEnd
is linked to the audio transfer completion. The buffer difference represent a circular distance between two indexes. If the distance is less than the lower limit, you have an underrun situation, that is the USB side does not produce fast enough the audio samples consumed by the codec. Conversely, if the distance is greater than the upper limit, this is an overrun situation, that is the USB side produces faster then the the codec can consume audio data. To keep the codec and USB in sync, a simple algorithm is used to add an audio sample in case of underrun and to remove a sample frame in case of overrun.
The frequency at which the playback stream correction is evaluated is configurable via the field CorrPeriodMs
of the structure USBD_AUDIO_STREAM_CFG
.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Figure - Adding a Sample in Case of Underrun illustrates the algorithm to add an audio sample in case of underrun situation.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||
---|---|---|
| ||
(1) Sample N is moved at N+1. (2) Sample N is rebuilt and equal to the average of N-1 and N+1. (3) The packet size is increased of one sample. |
The frequency at which the playback stream correction is evaluated is configurable via the field CorrPeriodMs
of the structure USBD_AUDIO_STREAM_CFG
.
Info |
---|
The stream correction supports signed PCM and unsigned PCM8 format. |
Warning |
---|
This stream correction is convenient for low-cost audio design. It will give good results as long as the incoming USB audio sampling frequency is very close to the DAC input clock frequency. However, if the difference between the two frequencies is important, this will add audio distortion. |
Figure - Removing a Sample in Case of Overrun illustrates the algorithm to remove an audio sample in case of overrun situation.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||
---|---|---|---|---|
| ||||
Panel | ||
---|---|---|
| ||
(1) Sample N-2 is rebuilt and equal to the average of N, N-1, N-2 and N-3. (2) Sample N is moved at N-1. (3) The packet size is reduced of one sample. |
The playback stream correction offers the possibility to apply your own correction algorithm. If an underrun or overrun situation is detected, an application callback is called. Listing - Example of Playback Correction Callback Provided by the Application shows an example of playback correction callback prototype and definition provided by the application.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||
---|---|---|---|---|
| ||||
static CPU_INT16U App_USBD_Audio_PlaybackCorr(USBD_AUDIO_AS_ALT_CFG *p_as_alt_cfg,
CPU_BOOLEAN underrun_flag,
void *p_buf,
CPU_INT16U buf_len_cur,
CPU_INT16U buf_len_total,
USBD_ERR *p_err);
CPU_BOOLEAN App_USBD_Audio_Init (CPU_INT08U dev_nbr,
CPU_INT08U cfg_hs,
CPU_INT08U cfg_fs)
{
...
speaker_playback_as_if_handle = USBD_Audio_AS_IF_Cfg(&USBD_SpeakerStreamCfg,
&USBD_AS_IF1_SpeakerCfg,
&USBD_Audio_DrvAS_API_Template,
DEF_NULL,
IT2_ID,
App_USBD_Audio_PlaybackCorr, (2)
&err);
if (err != USBD_ERR_NONE) {
/* $$$$ Handle the error. */
}
...
return (DEF_OK);
}
/*
*********************************************************************************************************
* App_USBD_Audio_PlaybackCorr()
*
* Description : Apply user-defined correction algorithm to the playback stream.
*
* Argument(s) : p_as_alt_cfg Pointer to AudioStreaming interface configuration structure.
*
* is_underrun Flag indicating if an underrun (audio clock faster than USB) or
* overrun (audio clock slower than USB) situation has been detected
* by the Audio class.
*
* p_buf Pointer to buffer to which the correction will be applied to.
*
* buf_len_cur Current length of the buffer.
*
* buf_len_total Total length of the buffer.
*
* p_err Pointer to variable that will receive the return error code from
* this function :
*
* USBD_ERR_NONE Correction successfully applied to buffer.
* Return(s) : New length of the buffer after correction.
*
* Caller(s) : USBD_Audio_PlaybackCorr().
*
* Note(s) : none.
*********************************************************************************************************
*/
(3)
static CPU_INT16U App_USBD_Audio_PlaybackCorr (USBD_AUDIO_AS_ALT_CFG *p_as_alt_cfg,
CPU_BOOLEAN is_underrun,
void *p_buf,
CPU_INT16U buf_len_cur,
CPU_INT16U buf_len_total,
USBD_ERR *p_err)
{
(void)&p_as_alt_cfg;
(void)&is_underrun;
(void)&p_buf;
(void)&buf_len_cur;
(void)&buf_len_total;
*p_err = USBD_ERR_NONE;
return (buf_len_cur);
} |
Panel | ||
---|---|---|
| ||
(1) Prototype of your playback correction callback. (2) Upon configuration of an AudioStreaming interface with the function (3) Definition of your playback correction callback. Once the playback is open by the host and the built-in playback correction is enabled (
Beside the AudioStreaming alternate setting configuration structure, you will know the situation type (underrun or overrun via |
Warning |
---|
If |
Record Built-In Stream Correction
There is also a built-in record stream correction active only when the constant USBD_AUDIO_CFG_RECORD_CORR_EN
is set to DEF_ENABLED
. As explained in the section Record Stream, when an isochronous IN transfer completes by calling the callback function USBD_Audio_RecordIsocCmpl()
, the stream correction is evaluated. The evaluation relies on monitoring the record ring buffer queue. Two thresholds are defined: a lower limit and an upper limit based on the same principle as shown in 101482827. For the record path, ProducerEnd
is linked to the audio transfer completion while ConsumerEnd
is linked to the USB transfer completion. This is the opposite of the playback. Moreover, the ring buffer queue scheme is common to the playback and record streams. And within the audio class, the definition of overrun and underrun situation is "USB-centric".
Consequently, if the lower limit is reached, you have an overrun situation, that is the USB side consumes a little bit faster than the the codec can produce. Conversely, the upper limit corresponds to an underrun situation, that is the USB side does not consume fast enough the audio samples produced by the codec. As opposed to the playback stream correction, no software algorithm is needed to add or remove an audio sample. The audio class will adjust the audio peripheral hardware by using the number of required record data bytes indicated by USBD_Audio_RecordBufGet()
. The correction is done implicitly by the audio peripheral hardware by directly getting the right number of audio samples (-1 sample frame or +1 sample frame) to accommodate the overrun or underrun situation.
The frequency at which the record stream correction is evaluated is configurable via the field CorrPeriodMs
of the structure USBD_AUDIO_STREAM_CFG
.
Playback Feedback Correction
The feedback correction (refer to section Feedback Endpoint for an overview of feedback) takes place when the configuration constant USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN
is set to DEF_ENABLED
and the AudioStreaming interface uses an isochronous OUT endpoint with asynchronous synchronization. As explained in section Playback Stream, the stream correction is evaluated in the function USBD_Audio_PlaybackCorrSynch()
before the playback task provides a ready buffer to the audio peripheral driver.
The feedback value evaluation relies on monitoring the playback ring buffer queue. Based on the same principle as the playback built-in correction, the buffer difference between the indexes ProducerEnd
and ConsumerEnd
is computed and gives the reflect at which the USB and codec clocks operate. The feedback monitoring starts only when the playback stream priming is done, that is when the audio class calls the audio peripheral driver function USBD_Audio_DrvStreamStart
. Once the feedback monitoring has started, the underrun or overrun situation requiring a feedback value to be sent to the host is evaluated using the method shown in Table - Feedback Monitoring.
Anchor | ||||
---|---|---|---|---|
|
Panel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The underrun situation occurs when the USB side is slower than the codec. In that case, depending how fast is the codec, the underrun situation could be light or heavy. The processing will adjust the feedback value by telling the host to add up to one sample per frame depending of the underrun degree. Similarly, the overrun situation occurs when the USB side is faster than the codec. In that case, depending how slow is the codec, the overrun situation could be light or heavy. The processing will adjust the feedback value by telling the host to remove up to one sample per frame depending of the overrun degree.
...
The feedback value update to the host is evaluated every refresh period. The refresh period is configurable via the field CorrPeriodMs
of the structure USBD_AUDIO_STREAM_CFG
. When the refresh period is reached, if there is a correction to apply, the feedback value update is sent to the host by calling the function USBD_IsocTxAsync()
. If there is no correction necessary, the audio class does not prepare an isochronous IN transfer. Thus when the host sends an IN token, a zero-length packet is sent by the device. The host interprets this zero-length packet as "continue to apply the previous valid feedback value". The feedback value is sent in 10.14 format.
...