UHSDR/UHSDR-active-devel/mchf-eclipse/drivers/usb/app/usbd_audio_if.c
2022-11-08 16:13:55 +01:00

518 lines
14 KiB
C

/**
******************************************************************************
* @file : usbd_audio_if.c
* @brief : Generic media access Layer.
******************************************************************************
*
* Copyright (c) 2017 STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for STMicroelectronics.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "usbd_audio_if.h"
/* USER CODE BEGIN INCLUDE */
#include "uhsdr_board.h"
#include "audio_driver.h"
/* USER CODE END INCLUDE */
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
* @{
*/
/** @defgroup USBD_AUDIO
* @brief usbd core module
* @{
*/
/** @defgroup USBD_AUDIO_Private_TypesDefinitions
* @{
*/
/* USER CODE BEGIN PRIVATE_TYPES */
/* USER CODE END PRIVATE_TYPES */
/**
* @}
*/
/** @defgroup USBD_AUDIO_Private_Defines
* @{
*/
/* USER CODE BEGIN PRIVATE_DEFINES */
/* USER CODE END PRIVATE_DEFINES */
/**
* @}
*/
/** @defgroup USBD_AUDIO_Private_Macros
* @{
*/
/* USER CODE BEGIN PRIVATE_MACRO */
/* USER CODE END PRIVATE_MACRO */
/**
* @}
*/
/** @defgroup USBD_AUDIO_IF_Private_Variables
* @{
*/
/* USER CODE BEGIN PRIVATE_VARIABLES */
/* USER CODE END PRIVATE_VARIABLES */
/**
* @}
*/
/** @defgroup USBD_AUDIO_IF_Exported_Variables
* @{
*/
extern USBD_HandleTypeDef hUsbDeviceFS;
/* USER CODE BEGIN EXPORTED_VARIABLES */
/* USER CODE END EXPORTED_VARIABLES */
/**
* @}
*/
/** @defgroup USBD_AUDIO_Private_FunctionPrototypes
* @{
*/
static int8_t AUDIO_Init_FS (uint32_t AudioFreq, uint32_t Volume, uint32_t options);
static int8_t AUDIO_DeInit_FS (uint32_t options);
static int8_t AUDIO_AudioCmd_FS (uint8_t* pbuf, uint32_t size, uint8_t cmd);
static int8_t AUDIO_VolumeCtl_FS (uint8_t vol);
static int8_t AUDIO_MuteCtl_FS (uint8_t cmd);
static int8_t AUDIO_PeriodicTC_FS (uint8_t cmd);
static int8_t AUDIO_GetState_FS (void);
static int8_t AUDIO_InVolumeCtl_FS (uint8_t vol);
/* Audio Machine States */
typedef enum
{
AUDIO_STATE_INACTIVE = 0x00,
AUDIO_STATE_ACTIVE,
AUDIO_STATE_PLAYING,
AUDIO_STATE_PAUSED,
AUDIO_STATE_STOPPED,
AUDIO_STATE_ERROR
} USB_AUDIO_MACHINE_State_t;
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
static USB_AUDIO_MACHINE_State_t AudioState = AUDIO_STATE_INACTIVE;
typedef enum
{
USB_DIG_AUDIO_OUT_INIT = 0,
USB_DIG_AUDIO_OUT_IDLE,
USB_DIG_ADUIO_OUT_TX,
} USB_AUDIO_State_t;
USB_AUDIO_State_t usb_dig_audio_state = USB_DIG_AUDIO_OUT_INIT;
/**
* @}
*/
/** @defgroup usbd_audio_out_if_Private_Functions
* @{
*/
#define USB_AUDIO_OUT_NUM_BUF 16
#define USB_AUDIO_OUT_PKT_SIZE (AUDIO_OUT_PACKET/2)
#define USB_AUDIO_OUT_BUF_SIZE (USB_AUDIO_OUT_NUM_BUF * USB_AUDIO_OUT_PKT_SIZE)
static volatile int16_t out_buffer[USB_AUDIO_OUT_BUF_SIZE]; //buffer for filtered PCM data from Recv.
static volatile uint16_t out_buffer_tail;
static volatile uint16_t out_buffer_head;
static volatile uint16_t out_buffer_overflow;
static volatile uint16_t out_buffer_underflow;
static void audio_out_put_buffer(int16_t sample)
{
uint32_t next_head = (out_buffer_head + 1) %USB_AUDIO_OUT_BUF_SIZE;
if (next_head != out_buffer_head)
{
out_buffer[out_buffer_head] = sample;
out_buffer_head = next_head;
}
else
{
// ok. We loose data now, should never ever happen, but so what
// will cause minor distortion if only a few bytes.
out_buffer_overflow++;
}
}
uint16_t audio_out_buffer_fill()
{
uint16_t temp_head = out_buffer_head;
return ((((temp_head < out_buffer_tail)?USB_AUDIO_OUT_BUF_SIZE:0) + temp_head) - out_buffer_tail);
}
volatile int16_t* audio_out_buffer_next_pkt(uint32_t len)
{
if (audio_out_buffer_fill() >= len)
{
return &out_buffer[out_buffer_tail];
}
else
{
return NULL;
}
}
static void audio_out_buffer_pop_pkt(volatile int16_t* ptr, uint32_t len)
{
if (ptr)
{
// there was data and pkt has been used
// free the space
out_buffer_tail = (out_buffer_tail+len)%USB_AUDIO_OUT_BUF_SIZE;
}
}
/* len is length in stereo samples */
void UsbdAudio_FillTxBuffer(AudioSample_t *buffer, uint32_t len)
{
volatile int16_t *pkt = audio_out_buffer_next_pkt(2*len);
static uint16_t fill_buffer = 1;
if (fill_buffer == 0 && pkt)
{
for (uint32_t idx = len; idx; idx--)
{
// the purpose is to place the USB input exactly as the I2S does
// which is weird if on F4 and 32 Bit transfers are done. (mixed endian)
// for all other systems we scale 16bit USB audio to 32bit
// on 16 bit audio nothing at all happens here.
buffer->l = I2S_Int16_2_AudioSample(*pkt++);
buffer->r = I2S_Int16_2_AudioSample(*pkt++);
buffer++;
}
audio_out_buffer_pop_pkt(pkt,2*len);
}
else
{
if (fill_buffer == 0)
{
out_buffer_underflow++;
fill_buffer = 1;
}
if (audio_out_buffer_next_pkt((USB_AUDIO_OUT_BUF_SIZE*2)/3) != NULL)
{
fill_buffer = 0;
}
// Deliver silence if not enough data is stored in buffer
for (uint32_t idx = len; idx; idx--)
{
buffer->l = 0;
buffer->r = 0;
buffer++;
}
}
}
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */
/**
* @}
*/
USBD_AUDIO_ItfTypeDef USBD_AUDIO_fops_FS =
{
AUDIO_Init_FS,
AUDIO_DeInit_FS,
AUDIO_AudioCmd_FS,
AUDIO_VolumeCtl_FS,
AUDIO_MuteCtl_FS,
AUDIO_PeriodicTC_FS,
AUDIO_GetState_FS,
AUDIO_InVolumeCtl_FS,
};
/* Private functions ---------------------------------------------------------*/
/**
* @brief AUDIO_Init_FS
* Initializes the AUDIO media low layer over USB FS IP
* @param AudioFreq: Audio frequency used to play the audio stream.
* @param Volume: Initial volume level (from 0 (Mute) to 100 (Max))
* @param options: Reserved for future use
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t AUDIO_Init_FS(uint32_t AudioFreq, uint32_t Volume, uint32_t options)
{
/* USER CODE BEGIN 0 */
AudioState = AUDIO_STATE_ACTIVE;
return (USBD_OK);
/* USER CODE END 0 */
}
/**
* @brief AUDIO_DeInit_FS
* DeInitializes the AUDIO media low layer
* @param options: Reserved for future use
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t AUDIO_DeInit_FS(uint32_t options)
{
/* USER CODE BEGIN 1 */
AudioState = AUDIO_STATE_INACTIVE;
return (USBD_OK);
/* USER CODE END 1 */
}
/**
* @brief AUDIO_AudioCmd_FS
* Handles AUDIO command.
* @param pbuf: Pointer to buffer of data to be sent
* @param size: Number of data to be sent (in bytes)
* @param cmd: Command opcode
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static uint16_t fill;
static int8_t AUDIO_AudioCmd_FS (uint8_t* pbuf, uint32_t size, uint8_t cmd)
{
/* USER CODE BEGIN 2 */
/* Check the current state */
if ((AudioState == AUDIO_STATE_INACTIVE) || (AudioState == AUDIO_STATE_ERROR))
{
AudioState = AUDIO_STATE_ERROR;
return USBD_FAIL;
}
switch (cmd)
{
/* Process the PLAY command ----------------------------*/
case AUDIO_CMD_START:
case AUDIO_CMD_PLAY:
/* If current state is Active or Stopped */
if ((AudioState == AUDIO_STATE_ACTIVE) || \
(AudioState == AUDIO_STATE_STOPPED) || \
(AudioState == AUDIO_STATE_PAUSED) || \
(AudioState == AUDIO_STATE_PLAYING))
{
if ((ts.txrx_mode == TRX_MODE_RX && (ts.rx_iq_source == RX_IQ_DIG || ts.rx_iq_source == RX_IQ_DIGIQ))
|| (ts.txrx_mode == TRX_MODE_TX && (ts.tx_audio_source == TX_AUDIO_DIG || ts.tx_audio_source == TX_AUDIO_DIGIQ)))
{
uint16_t* pkt = (uint16_t*)pbuf;
uint32_t count;
static bool too_high = false;
fill = audio_out_buffer_fill();
// USB_AUDIO_OUT_NUM_BUF * USB_AUDIO_OUT_PKT_SIZE
bool is_low_space = fill > (3*(USB_AUDIO_OUT_NUM_BUF/4) * USB_AUDIO_OUT_PKT_SIZE);
bool is_high_space = fill < (USB_AUDIO_OUT_NUM_BUF/4 * USB_AUDIO_OUT_PKT_SIZE);
bool is_high_ok = fill > (3*(USB_AUDIO_OUT_NUM_BUF/4) * USB_AUDIO_OUT_PKT_SIZE);
// bool is_low_ok = fill < ((USB_AUDIO_OUT_NUM_BUF/4) * USB_AUDIO_OUT_PKT_SIZE);
uint16_t num_samples = size/2;
if (is_high_ok)
{
too_high = false;
} else {
too_high = is_high_space;
}
if (is_low_space)
{
num_samples-=2;
}
for (count =0; count < num_samples; count++)
{
audio_out_put_buffer(pkt[count]);
}
if (too_high)
{
audio_out_put_buffer(pkt[num_samples-2]);
audio_out_put_buffer(pkt[num_samples-1]);
}
}
AudioState = AUDIO_STATE_PLAYING;
return USBD_OK;
}
else /* Not allowed command */
{
return USBD_FAIL;
}
break;
/* Process the STOP command ----------------------------*/
case AUDIO_CMD_STOP:
if (AudioState != AUDIO_STATE_PLAYING)
{
/* Unsupported command */
return USBD_FAIL;
}
else
{
AudioState = AUDIO_STATE_STOPPED;
return USBD_OK;
}
/* Process the PAUSE command ---------------------------*/
case AUDIO_CMD_PAUSE:
if (AudioState != AUDIO_STATE_PLAYING)
{
/* Unsupported command */
return USBD_FAIL;
}
else
{
AudioState = AUDIO_STATE_PAUSED;
return USBD_OK;
}
/* Unsupported command ---------------------------------*/
default:
return USBD_FAIL;
}
return USBD_OK;
/* USER CODE END 2 */
}
/**
* @brief AUDIO_VolumeCtl_FS
* Controls AUDIO Volume.
* @param vol: volume level (0..100)
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t AUDIO_VolumeCtl_FS (uint8_t vol)
{
/* USER CODE BEGIN 3 */
uint16_t source = ts.tx_audio_source == TX_AUDIO_DIGIQ?TX_AUDIO_DIGIQ:TX_AUDIO_DIG;
ts.tx_gain[source] = vol;
return (USBD_OK);
/* USER CODE END 3 */
}
/**
* @brief AUDIO_MuteCtl_FS
* Controls AUDIO Mute.
* @param cmd: command opcode
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t AUDIO_MuteCtl_FS (uint8_t cmd)
{
/* USER CODE BEGIN 4 */
return (USBD_OK);
/* USER CODE END 4 */
}
/**
* @brief AUDIO_PeriodicT_FS
* @param cmd: Command opcode
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t AUDIO_PeriodicTC_FS (uint8_t cmd)
{
/* USER CODE BEGIN 5 */
return (USBD_OK);
/* USER CODE END 5 */
}
/**
* @brief AUDIO_GetState_FS
* Gets AUDIO State.
* @param None
* @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
*/
static int8_t AUDIO_GetState_FS (void)
{
/* USER CODE BEGIN 6 */
return (USBD_OK);
/* USER CODE END 6 */
}
#if 0
/**
* @brief Manages the DMA full Transfer complete event.
* @param None
* @retval None
*/
void TransferComplete_CallBack_FS(void)
{
/* USER CODE BEGIN 7 */
USBD_AUDIO_Sync(&hUsbDeviceFS, AUDIO_OFFSET_FULL);
/* USER CODE END 7 */
}
/**
* @brief Manages the DMA Half Transfer complete event.
* @param None
* @retval None
*/
void HalfTransfer_CallBack_FS(void)
{
/* USER CODE BEGIN 8 */
USBD_AUDIO_Sync(&hUsbDeviceFS, AUDIO_OFFSET_HALF);
/* USER CODE END 8 */
}
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
/**
* @}
*/
/**
* @}
*/
#endif
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
static int8_t AUDIO_InVolumeCtl_FS (uint8_t vol)
{
/* Call low layer volume setting function */
ts.rx_gain[RX_AUDIO_DIG].value = vol;
return USBD_OK;
}