518 lines
14 KiB
C
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;
|
|
}
|
|
|