Description of problem
The audio data received when operating the NXP RT1050 as a USB Host with the Audio class does not represent the audio data spoken into a connected USB microphone device.
This issue exists not with just a single microphone or device, as I have tested three different types of USB microphones. I have independently tested the microphones with a PC to confirm they work correctly.
The data corruption has been confirmed by taking traces of the USB bus and comparing the data received on the RT1050 with the data seen on the bus.
Prior Requisite Details
I fixed a bug in the USB stack that misreports the received data length. The details of that problem and solution can be found in the USB Host Audio Stream Receive Data Length Misreporting thread.
Setup Details
My RT1050 is setup as a USB Audio Host. I created the USB Audio setup based off of the USB HID Mouse example. I included the state machine below that sets the control and streaming interfaces.
I'm able to receive audio data from a connected Full-Speed USB microphone by repeatedly calling USB_HostAudioStreamRecv.
Test Setup and Results
I have results from two different ways of using the USBHostAudioStreamRecv function that both result in receiving unexpected audio data. In these tests, I'm speaking into the microphone and collecting three seconds of audio data into a RAM buffer on the RT1050. At the same time, I'm collecting a trace of the USB communication using a Teledyne Advisor T3 USB Analyzer.
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) static uint8_t _AudioBuffer[AUDIO_BUFFER_SIZE];
status = USB_HostAudioStreamRecv(audioInstance->classHandle,
audioInstance->audioBuffer,
AUDIO_BUFFER_SIZE,
_HostAudioInCallback,
audioInstance);
Here are descriptions of the two modes of operation. The audio bin files are 16-bit, 16kHz PCM audio, which can be played using Audacity.
Mode 1: More than one packet (greater than 34 bytes) is requested for transfer at a time
USB Trace of Audio: microphone_4k_read_usb_trace.bin.zip
Received data on RT1050 read from RAM buffer: microphone_4k_read_ram_buffer.bin.zip
a) AUDIO_BUFFER_SIZE = 34
b) The received audio’s speech is audible, but there is significant noise
c) In this mode, the audio data on the bus is equivalent to the audio data of the source (microphone)
d) Comparing the received data to the source data, bytes are rearranged and values are different
Mode 2: Exactly one packet (34 bytes) is requested for transfer at a time
USB Trace of Audio: microphone_34_read_usb_trace.bin.zip
Received data on RT1050 read from RAM buffer: microphone_34_read_ram_buffer.bin.zip
a) AUDIO_BUFFER_SIZE = 4096
b) The received audio does not resemble speech
c) The audio data recorded on the bus does not match the audio data of the source (microphone)!
d) One isochronous transaction occurs every 3ms, instead of the expected 1ms (Full Speed)
e) It seems there’s a delay in the stack that is preventing the reads from happening fast enough
Code and Operation Description
Below are snippets of my software that implement the USB Audio Host, which I based off of the USB HID mouse example.
There are two tasks: one that calls the USB_HostEhciTaskFunction, and another that manages the USB application state. The application state task (_MicrophoneApplicationTask) blocks on a FreeRTOS Event Group bit, which is set in _HostAudioControlCallback and _HostAudioInCallback. This allows the _MicrophoneApplicationTask to immediately react to events and handle any state changes.
typedef struct USBHostAudioInstance
{
usb_host_configuration_handle configHandle;
usb_device_handle deviceHandle;
usb_host_class_handle classHandle;
usb_host_interface_handle streamingInterfaceHandle;
usb_host_interface_handle controlInterfaceHandle;
uint8_t deviceState;
uint8_t prevState;
uint8_t runState;
uint8_t runWaitState;
uint16_t maxPacketSize;
uint8_t *audioBuffer;
} USBHostAudioInstance_t;
typedef enum USBHostAudioRunState
{
USBHostAudioRunState_Idle = 0,
USBHostAudioRunState_SetControlInterface,
USBHostAudioRunState_WaitSetControlInterface,
USBHostAudioRunState_SetStreamingInterface,
USBHostAudioRunState_WaitSetStreamingInterface,
USBHostAudioRunState_ReceiveStream,
USBHostAudioRunState_WaitReceiveStream
} USBHostAudioRunState_t;
static void _HostAudioControlCallback(
void *param,
uint8_t *data,
uint32_t dataLength,
usb_status_t status)
{
UNUSED_PARAMETER(data);
UNUSED_PARAMETER(dataLength);
UNUSED_PARAMETER(status);
USBHostAudioInstance_t *audioInstance = (USBHostAudioInstance_t *)param;
if (audioInstance->runWaitState == USBHostAudioRunState_WaitSetControlInterface)
{
audioInstance->runState = USBHostAudioRunState_SetStreamingInterface;
}
else if (audioInstance->runWaitState == USBHostAudioRunState_WaitSetStreamingInterface)
{
audioInstance->runState = USBHostAudioRunState_GetSamplingFrequency;
}
else if (audioInstance->runWaitState == USBHostAudioRunState_WaitGetSamplingFrequency)
{
audioInstance->runState = USBHostAudioRunState_ReceiveStream;
}
else
{
DEBUG_USB("%s: Unhandled runWaitState\r\n", __func__);
}
xEventGroupSetBits(_USBEventGroup,
BIT_0);
}
static void _HostAudioInCallback(
void *param,
uint8_t *data,
uint32_t dataLength,
usb_status_t status)
{
USBHostAudioInstance_t *audioInstance = (USBHostAudioInstance_t *)param;
if (audioInstance->deviceState == kStatus_DEV_Attached)
{
if (status == kStatus_USB_Success)
{
VoiceProcessor_ReceivedDataCallback(data, dataLength);
if (audioInstance->runWaitState == USBHostAudioRunState_WaitReceiveStream)
{
audioInstance->runState = USBHostAudioRunState_ReceiveStream;
}
else
{
ASSERT_FAILURE();
}
}
else
{
ASSERT_FAILURE();
DEBUG_USB("%s: Receive unsuccessful\r\n", __func__);
}
}
else
{
ASSERT_FAILURE();
DEBUG_USB("%s: Device not attached\r\n", __func__);
}
xEventGroupSetBits(_USBEventGroup,
BIT_0);
}
static void _USBHostTask(void *param)
{
while (1)
{
USB_HostEhciTaskFunction(param);
}
}
static void _MicrophoneApplicationTask(void *param)
{
EventBits_t uxBits;
while (1)
{
uxBits = xEventGroupWaitBits(_USBEventGroup,
BIT_USBEvent,
pdTRUE,
pdFALSE,
portMAX_DELAY);
_HostAudioStateManager(param);
}
}
static void _HostAudioStateManager(void *param)
{
usb_status_t status;
USBHostAudioInstance_t *audioInstance = (USBHostAudioInstance_t *)param;
if (audioInstance->deviceState != audioInstance->prevState)
{
audioInstance->prevState = audioInstance->deviceState;
switch (audioInstance->deviceState)
{
case kStatus_DEV_Idle:
break;
case kStatus_DEV_Attached:
DEBUG_USB("Mic device attached\r\n");
audioInstance->runState = USBHostAudioRunState_SetControlInterface;
status = USB_HostAudioInit(audioInstance->deviceHandle, &audioInstance->classHandle);
if (status != kStatus_USB_Success)
{
DEBUG_USB("USB Host Audio class initialize failure\r\n");
ASSERT_FAILURE();
}
break;
case kStatus_DEV_Detached:
audioInstance->deviceState = kStatus_DEV_Idle;
audioInstance->runState = USBHostAudioRunState_Idle;
USB_HostAudioDeinit(audioInstance->deviceHandle,
audioInstance->classHandle);
audioInstance->classHandle = NULL;
DEBUG_USB("USB Audio device detached\r\n");
break;
default:
break;
}
}
switch (audioInstance->runState)
{
case USBHostAudioRunState_Idle:
break;
case USBHostAudioRunState_SetControlInterface:
audioInstance->runWaitState = USBHostAudioRunState_WaitSetControlInterface;
audioInstance->runState = USBHostAudioRunState_Idle;
status = USB_HostAudioControlSetInterface(audioInstance->classHandle,
audioInstance->controlInterfaceHandle,
DEVICE_CONTROL_ALTERNATE_SETTING,
_HostAudioControlCallback,
audioInstance);
ASSERT(status == kStatus_USB_Success);
DEBUG_USB("Set Control Interface Complete\r\n");
break;
case USBHostAudioRunState_SetStreamingInterface:
audioInstance->runWaitState = USBHostAudioRunState_WaitSetStreamingInterface;
audioInstance->runState = USBHostAudioRunState_Idle;
status = USB_HostAudioStreamSetInterface(audioInstance->classHandle,
audioInstance->streamingInterfaceHandle,
DEVICE_MIC_ALTERNATE_SETTING,
_HostAudioControlCallback,
audioInstance);
ASSERT(status == kStatus_USB_Success);
DEBUG_USB("Set Streaming Interface Complete\r\n");
break;
case USBHostAudioRunState_ReceiveStream:
audioInstance->runWaitState = USBHostAudioRunState_WaitReceiveStream;
audioInstance->runState = USBHostAudioRunState_Idle;
uint32_t maxPacketSize = USB_HostAudioPacketSize(audioInstance->classHandle,
USB_ENDPOINT_ISOCHRONOUS,
USB_IN);
ASSERT(AUDIO_BUFFER_SIZE >= maxPacketSize);
status = USB_HostAudioStreamRecv(audioInstance->classHandle,
audioInstance->audioBuffer,
AUDIO_BUFFER_SIZE,
_HostAudioInCallback,
audioInstance);
ASSERT(status == kStatus_USB_Success);
break;
default:
break;
}
}
fangfang