I introduced some new definitions in usbd_conf.h file, because I have to modify the whole project from stereo to 4 channels later.
/* AUDIO Class Config */
#define USBD_AUDIO_FREQ 48000
#define USBD_AUDIO_CHANNELS 2
#define USBD_AUDIO_BYTES 2
In stm32f4discovery_audio.h fileban there is a definition calledAUDIODATA SIZE, which still defines 16 bits, so it should be modified later if I want to go for 24 bits. (Which is unlikely)
I modified usbd_audio.c file in USB device library AUDIO class (Middleware…)
#define AUDIO_SAMPLE_FREQ(frq) (uint8_t)(frq), (uint8_t)((frq >> 8)), (uint8_t)((frq >> 16))
#define AUDIO_PACKET_SZE(frq,chs,bytes) (uint8_t)(((frq * chs * bytes)/1000) & 0xFF), \
(uint8_t)((((frq * chs * bytes)/1000) >> 8) & 0xFF)
And I am using these definitions in the descriptors
/* USB Speaker Audio Type III Format Interface Descriptor */
0x0B, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */
AUDIO_FORMAT_TYPE_III, /* bFormatType */ ///This could be AUDIO_FORMAT_TYPE_I also
USBD_AUDIO_CHANNELS, /* bNrChannels */
USBD_AUDIO_BYTES, /* bSubFrameSize : 2 Bytes per frame (16bits) */
USBD_AUDIO_BYTES*8, /* bBitResolution (16-bits per sample) */
0x01, /* bSamFreqType only one frequency supported */
AUDIO_SAMPLE_FREQ(USBD_AUDIO_FREQ), /* Audio sampling frequency coded on 3 bytes */
/* 11 byte*/
Calculating packet size in usbd_audio.h file:
#define AUDIO_OUT_PACKET (uint32_t)(((USBD_AUDIO_FREQ * USBD_AUDIO_BYTES * USBD_AUDIO_CHANNELS) /1000))
In usbd_audio.h, we can set up the number of buffered packets, which means the latency. It is 80 packets by default, which means 40ms latency I think. I modified it to be 16 packets = 8ms.
I modified the function which passed 2 times the necessary size for DMA.
I am going to use the audio data buffer as a double buffer.
In USBD_AUDIO_DataOut function, I am waiting for the USB to fill one half of the buffer.
After it filled the first half, I am going to send this part toh the I2S DMA, and the USB will write the second part during DMA transfer. And do the same when USB filled the second half, I’ll send it to the I2S and start the whole cycle.
We need to synch because if USB is faster, it can overwrite the DMA part, and if DMA is faster, it can run out o useful data. USB_Audio_Sync is invoket from DMA Transfer complete interrupt.
If USB write and DMA read pointers are closer than 2 audio data packets, the next DMA transfer will be 1 packet less or 1 packet longer. Depending on which process was faster.
I configured some LED feedback to see if we have some buffer overrun or underrun for DMA or USB, but after 25 minutes of playing, it worked correctly. However without this synchronization, the LED had toggled, so it proved me that this synch is required and works good enough for this phase of my project.
Later I might study the possibility of implementing a synch endpoint, that is mentioned in the standard and in some articles if found on the web.
The current state:
I have a working stereo soundcard, and I mostly understand what it does, because I rewrote it 🙂