536 lines
16 KiB
C

/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <math.h>
#include <string.h>
#include "esp_codec_dev.h"
#include "audio_codec_if.h"
#include "audio_codec_data_if.h"
#include "audio_codec_sw_vol.h"
#include "esp_log.h"
#define TAG "Adev_Codec"
#define VOL_TRANSITION_TIME (50)
typedef struct {
const audio_codec_if_t *codec_if;
const audio_codec_data_if_t *data_if;
const audio_codec_vol_if_t *sw_vol;
esp_codec_dev_type_t dev_caps;
bool input_opened;
bool output_opened;
int volume;
float mic_gain;
bool muted;
bool mic_muted;
bool sw_vol_alloced;
esp_codec_dev_vol_curve_t vol_curve;
bool disable_when_closed;
} codec_dev_t;
static bool _verify_codec_ready(codec_dev_t *dev)
{
if (dev->codec_if && dev->codec_if->is_open) {
if (dev->codec_if->is_open(dev->codec_if) == false) {
return false;
}
}
return true;
}
static bool _verify_drv_ready(codec_dev_t *dev, bool playback)
{
if (_verify_codec_ready(dev) == false) {
ESP_LOGE(TAG, "Codec is not open yet");
return false;
}
if (dev->data_if->is_open && dev->data_if->is_open(dev->data_if) == false) {
ESP_LOGE(TAG, "Codec data interface not open");
return false;
}
if (playback && dev->data_if->write == NULL) {
ESP_LOGE(TAG, "Need provide write API");
return false;
}
if (playback == false && dev->data_if->read == NULL) {
ESP_LOGE(TAG, "Need provide read API");
return false;
}
return true;
}
static int _verify_codec_setting(codec_dev_t *dev, bool playback)
{
if ((playback && (dev->dev_caps & ESP_CODEC_DEV_TYPE_OUT) == 0) ||
(!playback && (dev->dev_caps & ESP_CODEC_DEV_TYPE_IN) == 0)) {
return ESP_CODEC_DEV_NOT_SUPPORT;
}
if (_verify_codec_ready(dev) == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return ESP_CODEC_DEV_OK;
}
static int _get_default_vol_curve(esp_codec_dev_vol_curve_t *curve)
{
curve->vol_map = (esp_codec_dev_vol_map_t *) malloc(2 * sizeof(esp_codec_dev_vol_map_t));
if (curve->vol_map) {
curve->count = 2;
curve->vol_map[0].vol = 0;
curve->vol_map[0].db_value = -50.0;
curve->vol_map[1].vol = 100;
curve->vol_map[1].db_value = 0.0;
}
return ESP_CODEC_DEV_OK;
}
static float _get_vol_db(esp_codec_dev_vol_curve_t *curve, int vol)
{
if (vol == 0) {
return -96.0;
}
int n = curve->count;
if (n == 0) {
return 0.0;
}
if (vol >= curve->vol_map[n - 1].vol) {
return curve->vol_map[n - 1].db_value;
}
for (int i = 0; i < n - 1; i++) {
if (vol < curve->vol_map[i + 1].vol) {
if (curve->vol_map[i].vol != curve->vol_map[i + 1].vol) {
float ratio = (curve->vol_map[i + 1].db_value - curve->vol_map[i].db_value) /
(curve->vol_map[i + 1].vol - curve->vol_map[i].vol);
return curve->vol_map[i].db_value + (vol - curve->vol_map[i].vol) * ratio;
}
break;
}
}
return 0.0;
}
static void _update_codec_setting(codec_dev_t *dev)
{
esp_codec_dev_handle_t h = (esp_codec_dev_handle_t) dev;
if (dev->output_opened) {
esp_codec_dev_set_out_vol(h, dev->volume);
esp_codec_dev_set_out_mute(h, dev->muted);
}
if (dev->input_opened) {
esp_codec_dev_set_in_gain(h, dev->mic_gain);
esp_codec_dev_set_in_mute(h, dev->mic_muted);
}
}
esp_codec_dev_handle_t esp_codec_dev_new(esp_codec_dev_cfg_t *cfg)
{
if (cfg == NULL || cfg->data_if == NULL || cfg->dev_type == ESP_CODEC_DEV_TYPE_NONE) {
return NULL;
}
codec_dev_t *dev = (codec_dev_t *) calloc(1, sizeof(codec_dev_t));
if (dev == NULL) {
return NULL;
}
dev->dev_caps = cfg->dev_type;
dev->codec_if = cfg->codec_if;
dev->data_if = cfg->data_if;
if (cfg->dev_type & ESP_CODEC_DEV_TYPE_OUT) {
_get_default_vol_curve(&dev->vol_curve);
}
dev->disable_when_closed = true;
return (esp_codec_dev_handle_t) dev;
}
int esp_codec_dev_open(esp_codec_dev_handle_t handle, esp_codec_dev_sample_info_t *fs)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL || fs == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (dev->input_opened || dev->output_opened) {
ESP_LOGI(TAG, "Input already open");
return ESP_CODEC_DEV_OK;
}
if ((dev->dev_caps & ESP_CODEC_DEV_TYPE_IN)) {
// check record
if (_verify_drv_ready(dev, false) == false) {
ESP_LOGE(TAG, "Codec not support input");
} else {
dev->input_opened = true;
}
}
if ((dev->dev_caps & ESP_CODEC_DEV_TYPE_OUT)) {
// check record
if (_verify_drv_ready(dev, true) == false) {
ESP_LOGE(TAG, "Codec not support output");
} else {
dev->output_opened = true;
}
}
if (dev->input_opened == false && dev->output_opened == false) {
return ESP_CODEC_DEV_NOT_SUPPORT;
}
const audio_codec_if_t *codec = dev->codec_if;
const audio_codec_data_if_t *data_if = dev->data_if;
if (data_if->set_fmt) {
data_if->set_fmt(data_if, dev->dev_caps, fs);
}
if (data_if->enable) {
data_if->enable(data_if, dev->dev_caps, true);
}
if (codec) {
// TODO not set codec fs
if (codec->set_fs) {
if (codec->set_fs(codec, fs) != 0) {
return ESP_CODEC_DEV_NOT_SUPPORT;
}
}
if (codec->enable) {
if (codec->enable(codec, true) != ESP_CODEC_DEV_OK) {
ESP_LOGE(TAG, "Fail to enable codec");
return ESP_CODEC_DEV_DRV_ERR;
}
}
}
if (dev->output_opened) {
if (codec == NULL || codec->set_vol == NULL) {
if (dev->sw_vol == NULL) {
dev->sw_vol = audio_codec_new_sw_vol();
dev->sw_vol_alloced = true;
}
}
if (dev->sw_vol) {
dev->sw_vol->open(dev->sw_vol, fs, VOL_TRANSITION_TIME);
}
}
// update settings to avoid lost after re-enable
_update_codec_setting(dev);
ESP_LOGI(TAG, "Open codec device OK");
return ESP_CODEC_DEV_OK;
}
int esp_codec_dev_read_reg(esp_codec_dev_handle_t handle, int reg, int *val)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL || val == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (dev->codec_if && dev->codec_if->get_reg) {
return dev->codec_if->get_reg(dev->codec_if, reg, val);
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
int esp_codec_dev_write_reg(esp_codec_dev_handle_t handle, int reg, int val)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (dev->codec_if && dev->codec_if->set_reg) {
return dev->codec_if->set_reg(dev->codec_if, reg, val);
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
int esp_codec_dev_read(esp_codec_dev_handle_t handle, void *data, int len)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL || data == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (dev->input_opened == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
const audio_codec_data_if_t *data_if = dev->data_if;
if (data_if->read) {
return data_if->read(data_if, (uint8_t *) data, len);
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
int esp_codec_dev_write(esp_codec_dev_handle_t handle, void *data, int len)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL || data == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (dev->output_opened == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
const audio_codec_data_if_t *data_if = dev->data_if;
if (data_if->write) {
// Soft volume process firstly
if (dev->sw_vol) {
dev->sw_vol->process(dev->sw_vol, (uint8_t *) data, len, (uint8_t *) data, len);
}
return data_if->write(data_if, (uint8_t *) data, len);
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
int esp_codec_dev_set_vol_curve(esp_codec_dev_handle_t handle, esp_codec_dev_vol_curve_t *curve)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL || curve == NULL || curve->vol_map == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, true);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
int size = curve->count * sizeof(esp_codec_dev_vol_map_t);
esp_codec_dev_vol_map_t *new_map = (esp_codec_dev_vol_map_t *) realloc(dev->vol_curve.vol_map, size);
if (new_map == NULL) {
return ESP_CODEC_DEV_NO_MEM;
}
dev->vol_curve.vol_map = new_map;
memcpy(dev->vol_curve.vol_map, curve->vol_map, size);
dev->vol_curve.count = curve->count;
return ESP_CODEC_DEV_OK;
}
int esp_codec_dev_set_out_vol(esp_codec_dev_handle_t handle, int volume)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, true);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
const audio_codec_if_t *codec = dev->codec_if;
float db_value = _get_vol_db(&dev->vol_curve, volume);
dev->volume = volume;
// Prefer to use software volume setting
if (dev->sw_vol) {
dev->sw_vol->set_vol(dev->sw_vol, db_value);
return ESP_CODEC_DEV_OK;
}
if (codec && codec->set_vol) {
codec->set_vol(codec, db_value);
return ESP_CODEC_DEV_OK;
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
int esp_codec_dev_set_vol_handler(esp_codec_dev_handle_t handle, const audio_codec_vol_if_t *vol_handler)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL || vol_handler == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, true);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
if (dev->sw_vol == vol_handler) {
return ESP_CODEC_DEV_OK;
}
if (dev->sw_vol) {
if (dev->sw_vol_alloced) {
audio_codec_delete_vol_if(dev->sw_vol);
dev->sw_vol_alloced = false;
}
}
dev->sw_vol = vol_handler;
return ESP_CODEC_DEV_OK;
}
int esp_codec_dev_get_out_vol(esp_codec_dev_handle_t handle, int *volume)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, true);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
*volume = dev->volume;
return ESP_CODEC_DEV_OK;
}
int esp_codec_dev_set_out_mute(esp_codec_dev_handle_t handle, bool mute)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, true);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
const audio_codec_if_t *codec = dev->codec_if;
dev->muted = mute;
if (codec && codec->mute) {
codec->mute(codec, mute);
return ESP_CODEC_DEV_OK;
}
// When codec not support mute set volume instead
if (dev->sw_vol) {
float db_value = mute ? -100.0 : _get_vol_db(&dev->vol_curve, dev->volume);
dev->sw_vol->set_vol(dev->sw_vol, db_value);
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
int esp_codec_dev_get_out_mute(esp_codec_dev_handle_t handle, bool *muted)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, true);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
*muted = dev->muted;
return ESP_CODEC_DEV_OK;
}
int esp_codec_dev_set_in_gain(esp_codec_dev_handle_t handle, float db)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, false);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
const audio_codec_if_t *codec = dev->codec_if;
if (codec && codec->set_mic_gain) {
codec->set_mic_gain(codec, (int) db);
dev->mic_gain = db;
return ESP_CODEC_DEV_OK;
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
int esp_codec_dev_set_in_channel_gain(esp_codec_dev_handle_t handle, uint16_t channel_mask, float db)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL || channel_mask == 0) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, false);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
const audio_codec_if_t *codec = dev->codec_if;
if (codec && codec->set_mic_channel_gain) {
codec->set_mic_channel_gain(codec, channel_mask, (int) db);
return ESP_CODEC_DEV_OK;
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
int esp_codec_dev_get_in_gain(esp_codec_dev_handle_t handle, float *db_value)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, false);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
*db_value = dev->mic_gain;
return ESP_CODEC_DEV_OK;
}
int esp_codec_dev_set_in_mute(esp_codec_dev_handle_t handle, bool mute)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, false);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
const audio_codec_if_t *codec = dev->codec_if;
if (codec && codec->mute_mic) {
codec->mute_mic(codec, mute);
dev->mic_muted = mute;
return ESP_CODEC_DEV_OK;
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
int esp_codec_dev_get_in_mute(esp_codec_dev_handle_t handle, bool *muted)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = _verify_codec_setting(dev, false);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
*muted = dev->mic_muted;
return ESP_CODEC_DEV_OK;
}
int esp_codec_set_disable_when_closed(esp_codec_dev_handle_t handle, bool disable)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
dev->disable_when_closed = disable;
return ESP_CODEC_DEV_OK;
}
int esp_codec_dev_close(esp_codec_dev_handle_t handle)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (dev->output_opened == false && dev->input_opened == false) {
return ESP_CODEC_DEV_OK;
}
const audio_codec_if_t *codec = dev->codec_if;
if (dev->disable_when_closed && codec) {
if (codec->enable) {
codec->enable(codec, false);
}
}
const audio_codec_data_if_t *data_if = dev->data_if;
if (data_if->enable) {
data_if->enable(data_if, dev->dev_caps, false);
}
if (dev->sw_vol) {
dev->sw_vol->close(dev->sw_vol);
}
dev->output_opened = dev->input_opened = false;
return ESP_CODEC_DEV_OK;
}
void esp_codec_dev_delete(esp_codec_dev_handle_t handle)
{
codec_dev_t *dev = (codec_dev_t *) handle;
if (dev) {
esp_codec_dev_close(handle);
if (dev->vol_curve.vol_map) {
free(dev->vol_curve.vol_map);
}
// Only delete software vol when alloced internally
if (dev->sw_vol && dev->sw_vol_alloced) {
audio_codec_delete_vol_if(dev->sw_vol);
}
free(dev);
}
}
const char *esp_codec_dev_get_version(void)
{
return ESP_CODEC_DEV_VERSION;
}