AnsweredAssumed Answered

USB Host Audio Stream Receive Data Corruption

Question asked by Austin Wilhite on Nov 6, 2019
Latest reply on Nov 8, 2019 by Austin Wilhite

My RT1050 is setup as a USB Audio Host. I'm able to receive audio data from a connected Full-Speed USB microphone by repeatedly calling USB_HostAudioStreamRecv.

 

After fixing a bug in the USB stack that misreports the received data length, I've found that the data received via USB audio streaming does not represent the audio that is heard by the microphone. I have independently tested the microphone with a PC to confirm that it is working correctly.

 

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 Audio Buffer */

USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) static uint8_t _AudioBuffer[AUDIO_BUFFER_SIZE];

/* audioInstance->audioBuffer points to _AudioBuffer */

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

 

Can someone at NXP please attempt to reproduce this issue and correct it?

 

Below are snippets of my software that implement the USB Audio Host, which I based off of the USB 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.

 

/*! @brief USB host audio instance structure */
typedef struct USBHostAudioInstance
{
    usb_host_configuration_handle configHandle; /*!< the audio configuration handle */
    usb_device_handle deviceHandle;             /*!< the audio device handle */
    usb_host_class_handle classHandle;          /*!< the audio class handle */
    usb_host_interface_handle streamingInterfaceHandle;  /*!< the audio streaming interface handle */
    usb_host_interface_handle controlInterfaceHandle;    /*!< the audio control interface handle */
    uint8_t deviceState;                        /*!< device attach/detach status */
    uint8_t prevState;                          /*!< device attach/detach previous status */
    uint8_t runState;                           /*!< audio application run status */
    uint8_t runWaitState;   /*!< audio application wait status, go to next run status when the wait status success */
    uint16_t maxPacketSize; /*!< Interrupt in max packet size */
    uint8_t *audioBuffer;   /*!< use to receive data */
} USBHostAudioInstance_t;


/*! @brief host app run status */
typedef enum USBHostAudioRunState
{
    USBHostAudioRunState_Idle = 0,                      /*!< idle */
    USBHostAudioRunState_SetControlInterface,           /*!< execute set interface code */
    USBHostAudioRunState_WaitSetControlInterface,       /*!< wait set interface done */
    USBHostAudioRunState_SetStreamingInterface,         /*!< execute set interface code */
    USBHostAudioRunState_WaitSetStreamingInterface,     /*!< wait set interface done */
    USBHostAudioRunState_ReceiveStream,                 /*!< set interface is done, start streaming */
    USBHostAudioRunState_WaitReceiveStream              /*!< wait for streaming callback */
} 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,    /* The event group being updated. */
                        BIT_0);            /* The bits being set. */
}

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)
          {
               //DEBUG_USB("%s: Successfully received %u bytes\r\n", __func__, dataLength);

               VoiceProcessor_ReceivedDataCallback(data, dataLength);

               if (audioInstance->runWaitState == USBHostAudioRunState_WaitReceiveStream)
               {
                   audioInstance->runState = USBHostAudioRunState_ReceiveStream;
               }
               else
               {
                   /* unhandled condition */
                   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,    /* The event group being updated. */
                       BIT_0);            /* The bits being set. */
}


/***************************************************************************//**
    USB Host Task

    \param[in] param - Task input parameter
*/
/****************************************************************************/
static void _USBHostTask(void *param)
{
    while (1)
    {
        USB_HostEhciTaskFunction(param);
    }
}

/***************************************************************************//**
    USB Host Application Task

    \param[in] param - Task input parameter
*/
/****************************************************************************/
static void _MicrophoneApplicationTask(void *param)
{
    EventBits_t uxBits;

    while (1)
    {
        uxBits = xEventGroupWaitBits(_USBEventGroup,   /* The event group being tested. */
                                     BIT_USBEvent, /* The bits within the event group to wait for. */
                                     pdTRUE,        /* Cleared bits before returning. */
                                     pdFALSE,       /* Don't wait for both bits, either bit will do. */
                                     portMAX_DELAY);/* Wait forever. */

        _HostAudioStateManager(param);
    }
}

/***************************************************************************//**
    Task to handle the current device state

     \param[in] param - the device instance

*/
/****************************************************************************/
static void _HostAudioStateManager(void *param)
{
     usb_status_t status;
    USBHostAudioInstance_t *audioInstance = (USBHostAudioInstance_t *)param;

    /* Process device state changes (process once for each state) */

    if (audioInstance->deviceState != audioInstance->prevState)
    {
        audioInstance->prevState = audioInstance->deviceState;
        switch (audioInstance->deviceState)
        {
            case kStatus_DEV_Idle:
                break;

            /* Device is attached and enumeration is done */
            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;

            /* Device is detached */
            case kStatus_DEV_Detached:
                audioInstance->deviceState = kStatus_DEV_Idle;
                audioInstance->runState = USBHostAudioRunState_Idle;

                /* Audio class de-initialization */
                USB_HostAudioDeinit(audioInstance->deviceHandle,
                                  audioInstance->classHandle);

                audioInstance->classHandle = NULL;
                DEBUG_USB("USB Audio device detached\r\n");
                break;

            default:
                break;
        }
    }

    /* Process the run state */

    switch (audioInstance->runState)
    {
        case USBHostAudioRunState_Idle:
            break;

        /* Set the audio control interface */
        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;

        /* Set the audio streaming interface */
        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;
    }
}

 

 

 

Jorge Antonio Alcala Vazquez

Outcomes