cancel
Showing results for 
Search instead for 
Did you mean: 

USBX Audio Class 2.0 microphone won't send data to host

nico23
Senior III

I've developed on an STM32U5A9J-DK using ThreadX, which, via USBX, creates a loopback audio interface between the PC and the device in the way: PC->speaker on device->microphone on device->PC.

The devices are correctly seen and are working, as I'm able to enumerate them on the host and the device correctly receives audio frame,s and I'm able to process them into the device.

Now, the issue is that I'm unable to send the audio packets back to the host.

My implementation is pretty simple: I'm using the USBD_AUDIO_PlaybackStreamFrameDone function to read the audio frames and send them back to the microphone interface with

VOID USBD_AUDIO_PlaybackStreamFrameDone(UX_DEVICE_CLASS_AUDIO_STREAM *audio_play_stream,
                                        ULONG length)
{
  UCHAR *read_buffer;
  ULONG read_length;
  
  UCHAR *write_buffer;
  ULONG write_length;
  
  AUDIO_FRAME_MSG frame_msg;
  AUDIO_FRAME_BUFFER *frame_buf;

  /* -------------------------------------------------------------------------- */
  /* STEP 1: Get Access to the Received Frame (Speaker Data)                    */
  /* -------------------------------------------------------------------------- */
  if (ux_device_class_audio_read_frame_get(audio_play_stream, &read_buffer, &read_length) != UX_SUCCESS)
  {
      return; /* Should not happen if this callback is called */
  }

  /* Only process if we actually received bytes */
  if (length > 0 && read_length > 0)
  {
    /* ==================================================================== */
    /* LOGIC A: Send to TouchGFX Queue (User Application)                   */
    /* ==================================================================== */
    frame_buf = get_free_audio_frame();
    if (frame_buf != NULL && read_length <= MAX_AUDIO_FRAME_SIZE)
    {
      ux_utility_memory_copy(frame_buf->data, read_buffer, read_length);
      frame_buf->length = read_length;

      frame_msg.frame_ptr = frame_buf;
      
      if (tx_queue_send(&audio_processing_queue, &frame_msg, TX_NO_WAIT) != TX_SUCCESS)
      {
        release_audio_frame(frame_buf);
      }
    }

    /* ==================================================================== */
    /* LOGIC B: Loopback to Microphone (Direct Forwarding)                  */
    /* ==================================================================== */
    /* Check if Mic is active and stream is available */
    if (g_mic_stream != UX_NULL)
    {
        /* 2. Get Write Buffer: Request an empty buffer from Mic Stream FIFO */
        if (ux_device_class_audio_write_frame_get(g_mic_stream, &write_buffer, &write_length) == UX_SUCCESS)
        {
            /* 3. Process/Copy: Copy from Speaker Read Buffer to Mic Write Buffer */
            /* Ensure we don't overflow the Mic buffer (min of read/write lengths) */
            ULONG copy_len = (read_length < write_length) ? read_length : write_length;
            
            ux_utility_memory_copy(write_buffer, read_buffer, copy_len);

            /* 4. Commit Write: Tell Mic Stream the buffer is ready to send */
            ux_device_class_audio_write_frame_commit(g_mic_stream, copy_len);
        }
    }
  }

  /* -------------------------------------------------------------------------- */
  /* STEP 5: Free the Read Buffer (Crucial Step!)                               */
  /* -------------------------------------------------------------------------- */
  /* We must release the Speaker buffer so USBX can receive the next packet.    */
  ux_device_class_audio_read_frame_free(audio_play_stream);
}

(the first section is because I'm running a TouchGFX application as well, and the audio is sent to the GUI thread for visualization of the waveform, which works just fine)

For some reason, the ux_device_class_audio_write_frame_get(g_mic_stream, &write_buffer, &write_length) function always returns UX_BUFFER_OVERFLOW, even at the very first run when the micro is turned on (so the buffer should be empty)

I've tried to wipe the audio buffer of the microphone with

VOID USBD_AUDIO_RecordStreamChange(UX_DEVICE_CLASS_AUDIO_STREAM *audio_stream,
                                   ULONG alternate_setting)
{
  UX_SLAVE_ENDPOINT *endpoint;
  UCHAR *buffer;
  ULONG length;
  UINT status = UX_SUCCESS;

  if (alternate_setting == 0)
  {
    /* Stop Transmission */
    g_mic_stream = UX_NULL; // Invalidate the global handle

    endpoint = audio_stream->ux_device_class_audio_stream_endpoint;
    if (endpoint != UX_NULL)
    {
      _ux_device_stack_transfer_all_request_abort(endpoint, UX_ABORTED);
    }
  }
  else
  {
    /* Start Transmission */
    g_mic_stream = audio_stream; // Publish the stream handle

    /* Enable the class to accept write commands */
    status = ux_device_class_audio_transmission_start(audio_stream);

    if (ux_device_class_audio_write_frame_get(audio_stream, &buffer, &length) == UX_SUCCESS)
    {
        /* Fill with zeros (Silence) */
        ux_utility_memory_set(buffer, 0, length);
        
        /* Commit the silent frame so the thread has something to send */
        ux_device_class_audio_write_frame_commit(audio_stream, length);
    }

    if(status != UX_SUCCESS)
    	return;
  }
}

In this case, ux_device_class_audio_write_frame_get works for about two cycles (with the commit function being called), but on the host, no audio is received, and after the two cycles, it returns the UX_BUFFER_OVERFLOW issue.

Because I'm implementing a loopback interface, I have the same configuration (bitrate, channels, etc) on both IN and OUT interfaces, and the two FIFO queues have the same sizes (actually, I've tried to increase the microphone queue, as below, but got the same issue)

#define USBD_MAX_EP0_SIZE                             64U
#define USBD_AUDIO_REC_EPIN_HS_MPS                    28U  
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, USBD_MAX_EP0_SIZE/4);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, USBD_AUDIO_REC_EPIN_HS_MPS);

 in the same way, the parameters for the frame buffer are the same for both interfaces

// speaker
ux_device_class_audio_stream_parameter_max_frame_buffer_nb = 3U
ux_device_class_audio_stream_parameter_max_frame_buffer_size = 28U
// microphone
ux_device_class_audio_stream_parameter_max_frame_buffer_nb = 3U
ux_device_class_audio_stream_parameter_max_frame_buffer_size = 28U

The thing I noticed is that, in the speaker interface, I've defined  ux_device_class_audio_stream_parameter_thread_entry = ux_device_class_audio_read_thread_entry;  and if I pause the firmware, I'm seeing the audio thread for the speaker with a high number of executions (as it should be).

Instead, even if I defined ux_device_class_audio_stream_parameter_thread_entry = ux_device_class_audio_write_thread_entry; for some reason it seems the thread is never executed (count is always 0).

Any idea on why the audio packets are not sent to the host (probably why the audio thread for mthe icrophone is not executed)?

0 REPLIES 0