cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 USB Audio Device (microphone) with 24-bit resolution

Gabriel Melo
Associate III

As far as I know, STM32Cube does not offer support for creating an USB microphone (only speaker, as stated in the specification). However, I have managed to use the X-CUBE-MEMSMIC1 package to create an audio device, only replacing the usbd_audio.c and .h files.

The second problem that I have is that this package only offers support for 16-bit samples. By changing the descriptors below (in file usbd_audio_in.c), the device is recognized as a 24-bit device :

USBD_AUDIO_CfgDesc[index++] = 0x03;                                          /* bSubFrameSize */ //For 16: 0x02
  USBD_AUDIO_CfgDesc[index++] = 24;                                            /* bBitResolution */ // For 16: 16

To send the samples, we use a function called USBD_AUDIO_Data_Transfer :

/**
* @brief  USBD_AUDIO_Data_Transfer
*         Fills the USB internal buffer with audio data from user
* @param pdev: device instance
* @param audioData: audio data to be sent via USB
* @param dataAmount: number of PCM samples to be copyed
* @note Depending on the calling frequency, a coherent amount of samples must be passed to 
*       the function. E.g.: assuming a Sampling frequency of 16 KHz and 1 channel, 
*       you can pass 16 PCM samples if the function is called each millisecond, 
*       32 samples if called every 2 milliseconds and so on. 
* @retval status
*/
uint8_t  USBD_AUDIO_Data_Transfer(USBD_HandleTypeDef *pdev, int32_t * audioData, uint32_t PCMSamples)
{
  
  USBD_AUDIO_HandleTypeDef   *haudio;
  haudio = (USBD_AUDIO_HandleTypeDef *)pdev->pClassData;
  
  if(haudioInstance.state==STATE_USB_WAITING_FOR_INIT){    
    return USBD_BUSY;    
  }  
  uint16_t dataAmount = PCMSamples * 2; /*Bytes*/ // Maybe here?
  uint16_t true_dim = haudio->buffer_length;
  uint16_t current_data_Amount = haudio->dataAmount;
  uint16_t packet_dim = haudio->paketDimension;
  
  if(haudio->state==STATE_USB_REQUESTS_STARTED  || current_data_Amount!=dataAmount){   
    
    /*USB parameters definition, based on the amount of data passed*/
    haudio->dataAmount=dataAmount;                  
    uint16_t wr_rd_offset = (AUDIO_IN_PACKET_NUM/2) * dataAmount / packet_dim;
    haudio->wr_ptr=wr_rd_offset * packet_dim;
    haudio->rd_ptr = 0;
    haudio->upper_treshold = wr_rd_offset + 1;
    haudio->lower_treshold = wr_rd_offset - 1;
    haudio->buffer_length = (packet_dim * (dataAmount / packet_dim) * AUDIO_IN_PACKET_NUM);
    
    /*Memory allocation for data buffer, depending (also) on data amount passed to the transfer function*/
    if(haudio->buffer != NULL)
    {
      USBD_free(haudio->buffer);      
    }
    haudio->buffer = (uint8_t *)USBD_malloc(haudio->buffer_length + haudio->dataAmount);
    if(haudio->buffer == NULL)
    {
      return USBD_FAIL;       
    }
    memset(haudio->buffer,0,(haudio->buffer_length + haudio->dataAmount));
    haudio->state=STATE_USB_BUFFER_WRITE_STARTED;
    
    
  }else if(haudio->state==STATE_USB_BUFFER_WRITE_STARTED){
    if(haudio->timeout++==TIMEOUT_VALUE){
      haudio->state=STATE_USB_IDLE;
      ((USBD_AUDIO_ItfTypeDef *)pdev->pUserData)->Stop();   
     haudio->timeout=0;
    }
    memcpy((uint8_t * )&haudio->buffer[haudio->wr_ptr], (uint8_t *)(audioData), dataAmount);    
    haudio->wr_ptr += dataAmount;
    haudio->wr_ptr = haudio->wr_ptr % (true_dim);
    if((haudio->wr_ptr-dataAmount) == 0){
      memcpy((uint8_t *)(((uint8_t *)haudio->buffer)+true_dim),(uint8_t *)haudio->buffer, dataAmount);
    }
  }
  return USBD_OK;  
}

Which changes the state of the USB transfer and copies the data to the buffer. I've changed it to 32-bit input so I can use it with my 24-bit samples.

The function USBD_AUDIO_DataIn is also called and handles the low level :

/**
* @brief  USBD_AUDIO_DataIn
*         handle data IN Stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
static uint8_t USBD_AUDIO_DataIn (USBD_HandleTypeDef *pdev,
                                  uint8_t epnum)
{
  
  USBD_AUDIO_HandleTypeDef   *haudio;
  haudio = pdev->pClassData;
  uint32_t length_usb_pck;
  uint16_t app;
  uint16_t IsocInWr_app = haudio->wr_ptr;
  uint16_t true_dim = haudio->buffer_length;
  uint16_t packet_dim = haudio->paketDimension;
  uint16_t channels = haudio->channels;
  length_usb_pck = packet_dim;  
  haudio->timeout=0;
  if (epnum == (AUDIO_IN_EP & 0x7F))
  {    
    if (haudio->state == STATE_USB_IDLE) 
    {
      haudio->state=STATE_USB_REQUESTS_STARTED;
      ((USBD_AUDIO_ItfTypeDef *)pdev->pUserData)->Record();      
    }    
    if (haudio->state == STATE_USB_BUFFER_WRITE_STARTED)   
    {      
      haudio->rd_ptr = haudio->rd_ptr % (true_dim);              
      if(IsocInWr_app<haudio->rd_ptr){
        app = ((true_dim) - haudio->rd_ptr) +  IsocInWr_app;
      }else{
        app = IsocInWr_app - haudio->rd_ptr;
      }        
      if(app >= (packet_dim*haudio->upper_treshold)){       
        length_usb_pck += channels*2;
      }else if(app <= (packet_dim*haudio->lower_treshold)){
        length_usb_pck -= channels*2;
      }     
      USBD_LL_Transmit (pdev,AUDIO_IN_EP,
                        (uint8_t*)(&haudio->buffer[haudio->rd_ptr]),
                        length_usb_pck);      
      haudio->rd_ptr += length_usb_pck;      
 
      if(app < haudio->buffer_length/10)
      {
        ((USBD_AUDIO_ItfTypeDef *)pdev->pUserData)->Stop();
        haudio->state = STATE_USB_IDLE; 
        haudio->timeout=0;
        memset(haudio->buffer,0,(haudio->buffer_length + haudio->dataAmount));
      }       
    }
    else 
    {      
      USBD_LL_Transmit (pdev,AUDIO_IN_EP,
                        IsocInBuffDummy,
                        length_usb_pck);      
    }    
  }
  return USBD_OK;
}
 
/**
* @brief  USBD_AUDIO_EP0_RxReady
*         handle EP0 Rx Ready event
* @param  pdev: device instance
* @retval status
*/
 
static uint8_t  USBD_AUDIO_EP0_RxReady (USBD_HandleTypeDef *pdev)
{  
  USBD_AUDIO_HandleTypeDef   *haudio;
  haudio = pdev->pClassData;  
  if (haudio->control.cmd == AUDIO_REQ_SET_CUR)
  {    
    if (haudio->control.unit == AUDIO_OUT_STREAMING_CTRL)
    {
      ((USBD_AUDIO_ItfTypeDef *)pdev->pUserData)->VolumeCtl(VOL_CUR);    
      
      haudio->control.cmd = 0;
      haudio->control.len = 0;
      haudio->control.unit = 0;
      haudio->control.data[0]=0;
      haudio->control.data[0]=0;
    }
  }    
  return USBD_OK;
}

And finally, at the initialization, we define the packet size. As I noticed by looking at the X-CUBE-USB-AUDIO package, the packet size should be multiplied by 3 instead of 2 for 24-bit instead of 16 bit :

haudioInstance.paketDimension = (samplingFrequency/1000*Channels*3); // For 16: *2
  haudioInstance.frequency=samplingFrequency;
  haudioInstance.buffer_length = haudioInstance.paketDimension * AUDIO_IN_PACKET_NUM;
  haudioInstance.channels=Channels;  
  haudioInstance.upper_treshold = 5;
  haudioInstance.lower_treshold = 2;
  haudioInstance.state = STATE_USB_WAITING_FOR_INIT;
  haudioInstance.wr_ptr = 3 * haudioInstance.paketDimension;
  haudioInstance.rd_ptr = 0;  
  haudioInstance.dataAmount=0;
  haudioInstance.buffer = 0;

But when I change this my microphone simply stops working (though it is still recognized by the host). And when I keep the same parameters as the 16-bit I do not get good results for the exit.

My best guesses are that it has something to do with the buffer size (dataAmount) or the writing pointer wr_prt.

Does anyone know how to solve this?

Regards,

Gabriel

4 REPLIES 4
AA.10
Associate III

Hello,

I am trying to fix this issue also.

did someone solve it already?

Avi

ran into the same issue... looking for a fix...

Hi,

I found the answer,

In the function USBD_AUDIO_DataIn()

You should change the calculation of length_usb_pck to be depended on the resolution.

if(app >= (packet_dim*haudio->upper_treshold)){    

    length_usb_pck += channels*3;//2;

   }else if(app <= (packet_dim*haudio->lower_treshold)){

 length_usb_pck -= channels*3;//2;

Avi

Hi

i have the same problem so please can you include all the modification needed for 24 bits audio