1940 lines
66 KiB
C
1940 lines
66 KiB
C
/*
|
|
* Copyright (C) 2025 Amlogic Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#define LOG_TAG "audio_hw_utils_fast_mixer"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <stdint.h>
|
|
#include <dirent.h>
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <sys/time.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <stdbool.h>
|
|
#include <cutils/list.h>
|
|
#include <string.h>
|
|
#include "aml_malloc_debug.h"
|
|
#include "aml_dump_debug.h"
|
|
#include "aml_ringbuffer.h"
|
|
#include "audio_data_process.h"
|
|
#include "aml_audio_limiter.h"
|
|
#include "aml_volume_utils.h"
|
|
#include "aml_audio_log.h"
|
|
#include "aml_fast_mixer.h"
|
|
#include "aml_hwmixer_handle.h"
|
|
#include "aml_audio_ease.h"
|
|
#include "aml_audio_timer.h"
|
|
#include "format.h"
|
|
|
|
static int aml_hwmixer_pcm_mmap_get_hw_ptr(struct aml_hwmixer_handle *mixer,
|
|
unsigned int *hw_ptr, struct timespec *tstamp)
|
|
{
|
|
(void)mixer;
|
|
(void)hw_ptr;
|
|
(void)tstamp;
|
|
return -1;
|
|
}
|
|
|
|
static int aml_hwmixer_pcm_mmap_commit(struct aml_hwmixer_handle *mixer, unsigned int frames)
|
|
{
|
|
(void)mixer;
|
|
(void)frames;
|
|
return -1;
|
|
}
|
|
|
|
static int aml_hwmixer_pcm_get_buffer_size(struct aml_hwmixer_handle *mixer)
|
|
{
|
|
(void)mixer;
|
|
return -1;
|
|
}
|
|
|
|
static int aml_hwmixer_pcm_frames_to_bytes(struct aml_hwmixer_handle *mixer, unsigned int frames)
|
|
{
|
|
(void)mixer;
|
|
(void)frames;
|
|
return -1;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* Private struct or define, do not move to header file.
|
|
*******************************************************************/
|
|
|
|
#define AML_FAST_MIXER_INPORT_NUM 8
|
|
|
|
typedef enum AML_FAST_MIXER_INPORT_STATE {
|
|
AML_FAST_MIXER_INPORT_IDLE = 0,
|
|
AML_FAST_MIXER_INPORT_ACTIVE,
|
|
AML_FAST_MIXER_INPORT_PAUSING,
|
|
AML_FAST_MIXER_INPORT_PAUSING_FADE,
|
|
AML_FAST_MIXER_INPORT_PAUSED,
|
|
AML_FAST_MIXER_INPORT_RESUMING, // next stage will change to ACTIVE
|
|
AML_FAST_MIXER_INPORT_FLUSHING,
|
|
AML_FAST_MIXER_INPORT_FLUSHED,
|
|
AML_FAST_MIXER_INPORT_STOPPING,
|
|
AML_FAST_MIXER_INPORT_STOPPED,
|
|
AML_FAST_MIXER_INPORT_STANDBYING,
|
|
AML_FAST_MIXER_INPORT_STANDBYED,
|
|
|
|
AML_FAST_MIXER_INPORT_STATE_MAX,
|
|
} aml_fast_mixer_inport_state_t;
|
|
|
|
typedef struct aml_fast_mixer_inport_message {
|
|
aml_fast_mixer_msg_t msg_what;
|
|
struct listnode list;
|
|
} aml_fast_mixer_inport_message_t;
|
|
|
|
typedef struct fast_mixer_format_buffer {
|
|
void *buf_ptr;
|
|
size_t buf_size;
|
|
audio_format_t format;
|
|
int valid_samples;
|
|
} fast_mixer_format_buffer_t;
|
|
|
|
typedef struct aml_fast_mixer_buffer {
|
|
struct ring_buffer rbuffer;
|
|
struct pcm *mixer_handle;
|
|
struct pcm_config mixer_pcm_config;
|
|
struct aml_mixer_handle alsa_mixer;
|
|
audio_resample_config_t resample_config;
|
|
aml_audio_resample_t *resample_handle;
|
|
aml_pcm_downmix_st *downmixer;
|
|
fast_mixer_format_buffer_t format_buf;
|
|
} aml_fast_mixer_buffer_st;
|
|
|
|
typedef struct aml_fast_mixer_inport {
|
|
aml_fast_mixer_inport_param_st param;
|
|
aml_fast_mixer_buffer_st buffer;
|
|
aml_fast_mixer_inport_state_t state;
|
|
int port_id;
|
|
uint64_t drop_frames;
|
|
uint64_t last_frames_position;
|
|
struct timespec lasttimestamp;
|
|
pthread_mutex_t apts_lock;
|
|
aml_audio_ease_t *vol_ease;
|
|
struct listnode msg_list;
|
|
pthread_mutex_t msg_lock;
|
|
aml_audio_ease_t *fade_ease;
|
|
|
|
// Temporary interleaved data buffer
|
|
void *data_bufptr;
|
|
size_t data_buflen;
|
|
int data_frames;
|
|
bool data_valid;
|
|
|
|
// Temporary planar data buffer
|
|
aml_planar_buf_st data_planar_buf;
|
|
|
|
int64_t write_time_us;
|
|
float volume;
|
|
bool use_hw_mixer;
|
|
bool first_read;
|
|
bool mute;
|
|
bool standby_info_show; // used for debug or logcat info
|
|
struct aml_hwmixer_handle hwmixer_hdl;
|
|
} aml_fast_mixer_inport_st;
|
|
|
|
typedef struct aml_fast_mixer {
|
|
pthread_mutex_t lock;
|
|
aml_fast_mixer_inport_st *inports[AML_FAST_MIXER_INPORT_NUM];
|
|
aml_pcm_mixing_st *sw_mixer[AML_FAST_MIXER_PCM_MAX];
|
|
char name[FAST_MIXER_NAME_MAX_LEN];
|
|
bool initialized;
|
|
bool drop_enable;
|
|
bool data_used;
|
|
bool data_is_planar;
|
|
bool limiter_enable;
|
|
} aml_fast_mixer_st;
|
|
|
|
|
|
static const char *fast_mixer_port_msg_string[AML_FAST_MIXER_MSG_CNT_MAX] = {
|
|
"MSG_PAUSE",
|
|
"MSG_RESUME",
|
|
"MSG_FLUSH",
|
|
"MSG_STOP",
|
|
"MSG_STANDBY"
|
|
};
|
|
|
|
static int fast_mixer_remove_inport_all_messages(aml_fast_mixer_inport_st *port);
|
|
|
|
|
|
|
|
/* alloc new fast mixer */
|
|
void* aml_fast_mixer_new(const char *name, bool data_is_planar)
|
|
{
|
|
int i = 0;
|
|
aml_fast_mixer_st *mixer = NULL;
|
|
aml_pcm_mixing_st *sw_mixer = NULL;
|
|
|
|
mixer = (aml_fast_mixer_st *)aml_audio_calloc(1, sizeof(aml_fast_mixer_st));
|
|
if (mixer == NULL) {
|
|
AM_LOGE("calloc aml_fast_mixer_st failed !");
|
|
return NULL;
|
|
}
|
|
pthread_mutex_init(&mixer->lock, NULL);
|
|
|
|
for (i = 0; i < AML_FAST_MIXER_PCM_MAX; i++) {
|
|
sw_mixer = (aml_pcm_mixing_st *)aml_audio_calloc(1, sizeof(aml_pcm_mixing_st));
|
|
if (sw_mixer == NULL) {
|
|
AM_LOGE("calloc aml_pcm_mixing_st failed !");
|
|
goto error;
|
|
}
|
|
mixer->sw_mixer[i] = sw_mixer;
|
|
|
|
// planar data only use the first mixer;
|
|
if (data_is_planar) {
|
|
break;
|
|
}
|
|
}
|
|
if (name != NULL) {
|
|
snprintf(mixer->name, sizeof(mixer->name)-1, "fmixer_%s", name);
|
|
} else {
|
|
snprintf(mixer->name, sizeof(mixer->name)-1, "fmixer_anon");
|
|
}
|
|
|
|
mixer->drop_enable = false;
|
|
mixer->limiter_enable = true;
|
|
mixer->data_is_planar = data_is_planar;
|
|
AM_LOGI("%s limiter_enable, data_is_planar %d, success", mixer->name, data_is_planar);
|
|
mixer->initialized = true;
|
|
return mixer;
|
|
|
|
error:
|
|
for (i = 0; i < AML_FAST_MIXER_PCM_MAX; i++) {
|
|
if (mixer->sw_mixer[i] != NULL) {
|
|
aml_audio_free(mixer->sw_mixer[i]);
|
|
mixer->sw_mixer[i] = NULL;
|
|
}
|
|
}
|
|
aml_audio_free(mixer);
|
|
mixer = NULL;
|
|
return mixer;
|
|
}
|
|
|
|
/* release fast mixer */
|
|
void aml_fast_mixer_free(void *fast_mixer)
|
|
{
|
|
int port_id = 0;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
AM_LOGI("enter");
|
|
if (mixer == NULL) {
|
|
AM_LOGE("fast_mixer is NULL");
|
|
return;
|
|
}
|
|
|
|
for (port_id = 0; port_id < AML_FAST_MIXER_INPORT_NUM; port_id++) {
|
|
if (mixer->inports[port_id] != NULL) {
|
|
aml_fast_mixer_inport_remove(mixer, port_id);
|
|
}
|
|
}
|
|
|
|
pthread_mutex_lock(&mixer->lock);
|
|
mixer->initialized = false;
|
|
for (port_id = 0; port_id < AML_FAST_MIXER_PCM_MAX; port_id++) {
|
|
if (mixer->sw_mixer[port_id] != NULL) {
|
|
deinit_aml_pcm_mixer(mixer->sw_mixer[port_id]);
|
|
aml_audio_free(mixer->sw_mixer[port_id]);
|
|
mixer->sw_mixer[port_id] = NULL;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
|
|
pthread_mutex_destroy(&mixer->lock);
|
|
aml_audio_free(fast_mixer);
|
|
AM_LOGI("success");
|
|
}
|
|
|
|
static bool aml_fast_mixer_init_buffer(aml_fast_mixer_inport_st *input_port, const aml_fast_mixer_inport_param_st *param)
|
|
{
|
|
int ret = 0;
|
|
int rbuffer_size = 0;
|
|
bool use_sw_mixer = true;
|
|
aml_fast_mixer_buffer_st *mix_buffer = NULL;
|
|
fast_mixer_format_buffer_t *p_format_buf = NULL;
|
|
|
|
if (input_port == NULL || param == NULL) {
|
|
return false;
|
|
}
|
|
mix_buffer = &input_port->buffer;
|
|
|
|
if (param->mixer_type == AML_FAST_MIX_TYPE_SW) {
|
|
input_port->use_hw_mixer = false;
|
|
use_sw_mixer = true;
|
|
} else if (param->mixer_type == AML_FAST_MIX_TYPE_HW) {
|
|
input_port->use_hw_mixer = true;
|
|
use_sw_mixer = false;
|
|
input_port->hwmixer_hdl.param.mmap_type = AML_HWMIXER_MMAP_NONE; //normal type
|
|
} else if (param->mixer_type == AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
input_port->use_hw_mixer = true;
|
|
use_sw_mixer = false;
|
|
input_port->hwmixer_hdl.param.mmap_type = AML_HWMIXER_MMAP_NOIRQ; //mmap noirq type
|
|
}
|
|
|
|
// init hwmixer handle if use hw mixer
|
|
if (input_port->use_hw_mixer) {
|
|
ret = aml_hwmixer_handle_init(&input_port->hwmixer_hdl, &input_port->param.config);
|
|
if (0 != ret) {
|
|
AM_LOGD("aml_hwmixer_handle_init fail and back to AML_FAST_MIX_TYPE_SW");
|
|
use_sw_mixer = true;
|
|
} else {
|
|
input_port->use_hw_mixer = true;
|
|
use_sw_mixer = false;
|
|
}
|
|
}
|
|
|
|
// init ringbuffer if use sw mixer
|
|
if (use_sw_mixer && (param->buffer_frame_count > 0)) {
|
|
rbuffer_size = param->buffer_frame_count * param->config.frame_size;
|
|
ret = ring_buffer_init(&mix_buffer->rbuffer, rbuffer_size);
|
|
if (ret != 0) {
|
|
AM_LOGE("ring_buffer_init failed");
|
|
return false;
|
|
}
|
|
} else {
|
|
memset(&mix_buffer->rbuffer, 0, sizeof(mix_buffer->rbuffer));
|
|
}
|
|
p_format_buf = &mix_buffer->format_buf;
|
|
p_format_buf->buf_ptr = NULL;
|
|
p_format_buf->buf_size = 0;
|
|
p_format_buf->valid_samples = 0;
|
|
p_format_buf->format = AUDIO_FORMAT_INVALID;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void aml_fast_mixer_deinit_buffer(aml_fast_mixer_inport_st *input_port)
|
|
{
|
|
aml_fast_mixer_buffer_st *mix_buffer = NULL;
|
|
fast_mixer_format_buffer_t *p_format_buf = NULL;
|
|
if (input_port == NULL) {
|
|
return;
|
|
}
|
|
mix_buffer = &input_port->buffer;
|
|
|
|
/* deinit resource of hw mixer */
|
|
if (input_port->use_hw_mixer) {
|
|
if (aml_hwmixer_handle_deinit(&input_port->hwmixer_hdl)) {
|
|
AM_LOGE("aml_hwmixer_handle_deinit error!");
|
|
}
|
|
/* deinit resource of sw mixer */
|
|
} else {
|
|
ring_buffer_release(&mix_buffer->rbuffer);
|
|
memset(&mix_buffer->resample_config, 0, sizeof(mix_buffer->resample_config));
|
|
if (mix_buffer->resample_handle) {
|
|
aml_audio_resample_close(mix_buffer->resample_handle);
|
|
mix_buffer->resample_handle = NULL;
|
|
}
|
|
}
|
|
|
|
if (mix_buffer->downmixer) {
|
|
deinit_aml_pcm_downmix(mix_buffer->downmixer);
|
|
aml_audio_free(mix_buffer->downmixer);
|
|
mix_buffer->downmixer = NULL;
|
|
}
|
|
|
|
p_format_buf = &mix_buffer->format_buf;
|
|
if (p_format_buf->buf_ptr) {
|
|
aml_audio_free(p_format_buf->buf_ptr);
|
|
p_format_buf->buf_ptr = NULL;
|
|
p_format_buf->buf_size = 0;
|
|
p_format_buf->valid_samples = 0;
|
|
p_format_buf->format = AUDIO_FORMAT_INVALID;
|
|
}
|
|
}
|
|
|
|
static void aml_fast_mixer_logParameters(const aml_fast_mixer_inport_param_st *param)
|
|
{
|
|
if (param == NULL) {
|
|
return;
|
|
}
|
|
|
|
// This is very helpful for debugging in the future. Please leave it in.
|
|
AM_LOGI("config : sampleRate = %6d, channelCnt = %d, channelMask = %#x, format = 0x%x, frame_size = %d",
|
|
param->config.sampleRate, param->config.channelCnt, param->config.channelMask,
|
|
param->config.format, param->config.frame_size);
|
|
|
|
AM_LOGI("mixer_type = %d, estimate_buffer_level = %d, start_threshold_frame = %d, buffer_frame_count = %d",
|
|
param->mixer_type, param->estimate_buffer_level, param->start_threshold_frame,
|
|
param->buffer_frame_count);
|
|
|
|
AM_LOGI("encoder_exclusive = %d, disable_fading = %d", param->encoder_exclusive, param->disable_fading);
|
|
|
|
/* info of hwmixer */
|
|
if (param->mixer_type == AML_FAST_MIX_TYPE_HW ||
|
|
param->mixer_type == AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
AM_LOGI("hw_mixer_config : sampleRate = %d, channelCnt = %d, format = %d",
|
|
param->hw_mixer_config.rate, param->hw_mixer_config.channels, param->hw_mixer_config.format);
|
|
AM_LOGI("hw_mixer_config : period_size = %d, period_count = %d, start_threshold = %lu, stop_threshold = %lu",
|
|
param->hw_mixer_config.period_size, param->hw_mixer_config.period_count,
|
|
param->hw_mixer_config.start_threshold, param->hw_mixer_config.stop_threshold);
|
|
}
|
|
|
|
AM_LOGI("read_callback = %p, callback_data = %p, desc = (%s)",
|
|
param->read_callback, param->callback_data, param->desc_info);
|
|
}
|
|
|
|
int aml_fast_mixer_inport_add(void *fast_mixer, const aml_fast_mixer_inport_param_st *param)
|
|
{
|
|
int port_id = 0;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
aml_fast_mixer_inport_st *new_port = NULL;
|
|
const struct audioCfg *input_cfg = NULL;
|
|
|
|
if (mixer == NULL || !mixer->initialized || param == NULL) {
|
|
AM_LOGE("mixer %p or not initialized, param %p", mixer, param);
|
|
return -1;
|
|
}
|
|
if (!param->read_callback && param->buffer_frame_count <= 0
|
|
&& param->mixer_type != AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
AM_LOGE("invalid param : read_callback is NULL, bufferFrameCount %d", param->buffer_frame_count);
|
|
return -1;
|
|
}
|
|
|
|
AM_LOGI("----------------------------------->");
|
|
aml_fast_mixer_logParameters(param);
|
|
AM_LOGI("<-----------------------------------");
|
|
|
|
input_cfg = ¶m->config;
|
|
if (input_cfg->sampleRate == 0|| input_cfg->channelCnt == 0
|
|
|| !audio_channel_mask_is_valid(input_cfg->channelMask)
|
|
|| !audio_is_linear_pcm(input_cfg->format)
|
|
|| input_cfg->frame_size == 0) {
|
|
AM_LOGE("invalid config : sampleRate %d, channelCnt %d, channelMask 0x%x, format %d, frame_size %d",
|
|
input_cfg->sampleRate, input_cfg->channelCnt, input_cfg->channelMask, input_cfg->format, input_cfg->frame_size);
|
|
return -1;
|
|
}
|
|
|
|
pthread_mutex_lock(&mixer->lock);
|
|
for (port_id = 0; port_id < AML_FAST_MIXER_INPORT_NUM; port_id++) {
|
|
if (mixer->inports[port_id] == NULL) {
|
|
break;
|
|
} else {
|
|
const char *p_desc = mixer->inports[port_id]->param.desc_info;
|
|
AM_LOGI("%s, exist inports[%d] %s", mixer->name, port_id, p_desc);
|
|
}
|
|
}
|
|
if (port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("%s can not find avail input port", mixer->name);
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
return -1;
|
|
}
|
|
|
|
new_port = (aml_fast_mixer_inport_st *)aml_audio_calloc(1, sizeof(aml_fast_mixer_inport_st));
|
|
if (new_port == NULL) {
|
|
AM_LOGE("calloc %zu bytes fail", sizeof(aml_fast_mixer_inport_st));
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
return -1;
|
|
}
|
|
mixer->inports[port_id] = new_port;
|
|
new_port->state = AML_FAST_MIXER_INPORT_IDLE;
|
|
new_port->port_id = port_id;
|
|
memcpy(&new_port->param, param, sizeof(*param));
|
|
new_port->param.desc_info[sizeof(param->desc_info)-1] = '\0';
|
|
|
|
pthread_mutex_init(&new_port->apts_lock, NULL);
|
|
pthread_mutex_init(&new_port->msg_lock, NULL);
|
|
list_init(&new_port->msg_list);
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
|
|
if (aml_audio_ease_init(&new_port->vol_ease) != 0) {
|
|
AM_LOGE("init vol_ease fail !");
|
|
goto error_init_vol_ease;
|
|
}
|
|
new_port->volume = 1.0f;
|
|
new_port->mute = false;
|
|
new_port->vol_ease->start_volume = 1.0f;
|
|
new_port->vol_ease->current_volume = 1.0f;
|
|
new_port->vol_ease->target_volume = 1.0f;
|
|
new_port->vol_ease->do_easing = false;
|
|
new_port->data_valid = false;
|
|
new_port->data_planar_buf.data_valid = false;
|
|
new_port->standby_info_show = false;
|
|
|
|
if (!aml_fast_mixer_init_buffer(new_port, param)) {
|
|
AM_LOGE("aml_fast_mixer_init_buffer fail !");
|
|
goto error_init_buffer;
|
|
}
|
|
new_port->first_read = true;
|
|
new_port->state = AML_FAST_MIXER_INPORT_IDLE;
|
|
AM_LOGI("%s port %d success !", mixer->name, port_id);
|
|
return port_id;
|
|
|
|
error_init_buffer:
|
|
aml_audio_ease_close(new_port->vol_ease);
|
|
new_port->vol_ease = NULL;
|
|
error_init_vol_ease:
|
|
pthread_mutex_lock(&mixer->lock);
|
|
aml_audio_free(new_port);
|
|
mixer->inports[port_id] = NULL;
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
return -1;
|
|
}
|
|
|
|
|
|
int aml_fast_mixer_inport_remove(void *fast_mixer, int port_id)
|
|
{
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
char desc_info[FAST_MIXER_INPORT_DESC_MAX_LEN];
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
if (port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("invalid inport_id %d", port_id);
|
|
return -1;
|
|
}
|
|
|
|
pthread_mutex_lock(&mixer->lock);
|
|
input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
return -1;
|
|
}
|
|
mixer->inports[port_id] = NULL;
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
|
|
memset(desc_info, 0, sizeof(desc_info));
|
|
memcpy(desc_info, input_port->param.desc_info, sizeof(desc_info)-1);
|
|
aml_audio_ease_close(input_port->vol_ease);
|
|
input_port->vol_ease = NULL;
|
|
|
|
if (input_port->fade_ease != NULL) {
|
|
aml_audio_ease_close(input_port->fade_ease);
|
|
input_port->fade_ease = NULL;
|
|
}
|
|
if (input_port->data_bufptr != NULL) {
|
|
aml_audio_free(input_port->data_bufptr);
|
|
input_port->data_bufptr = NULL;
|
|
input_port->data_buflen = 0;
|
|
}
|
|
aml_planar_free_buf(&input_port->data_planar_buf);
|
|
fast_mixer_remove_inport_all_messages(input_port);
|
|
aml_fast_mixer_deinit_buffer(input_port);
|
|
aml_audio_free(input_port);
|
|
AM_LOGI("%s port %d (%s) ok !", mixer->name, port_id, desc_info);
|
|
return 0;
|
|
}
|
|
|
|
static const char *fast_mixer_inport_msg_to_str(aml_fast_mixer_msg_t msg)
|
|
{
|
|
if (msg < 0 || msg >= AML_FAST_MIXER_MSG_CNT_MAX) {
|
|
return "invalid !";
|
|
}
|
|
return fast_mixer_port_msg_string[msg];
|
|
}
|
|
|
|
void aml_fast_mixer_inport_send_message(void *fast_mixer, int port_id, aml_fast_mixer_msg_t msg)
|
|
{
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
aml_fast_mixer_inport_message_t *p_msg = NULL;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return;
|
|
}
|
|
if (port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("invalid port_id %d", port_id);
|
|
return;
|
|
}
|
|
if (msg < 0 || msg >= AML_FAST_MIXER_MSG_CNT_MAX) {
|
|
AM_LOGE("port_id %d, invalid message %d", port_id, msg);
|
|
return;
|
|
}
|
|
|
|
p_msg = aml_audio_calloc(1, sizeof(aml_fast_mixer_inport_message_t));
|
|
if (p_msg == NULL) {
|
|
AM_LOGE("port_id %d, no memory, size:%zu", port_id, sizeof(aml_fast_mixer_inport_message_t));
|
|
return;
|
|
}
|
|
p_msg->msg_what = msg;
|
|
|
|
/* Hold mixer->lock across lookup and enqueue so inport_remove cannot
|
|
* free the port while we still touch msg_list.
|
|
*/
|
|
pthread_mutex_lock(&mixer->lock);
|
|
input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
aml_audio_free(p_msg);
|
|
AM_LOGE("port_id %d, input port is NULL", port_id);
|
|
return;
|
|
}
|
|
pthread_mutex_lock(&input_port->msg_lock);
|
|
list_add_tail(&input_port->msg_list, &p_msg->list);
|
|
pthread_mutex_unlock(&input_port->msg_lock);
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
|
|
AM_LOGI("%s port_id %d, %s", mixer->name, port_id, fast_mixer_inport_msg_to_str(msg));
|
|
}
|
|
|
|
static aml_fast_mixer_inport_message_t *fast_mixer_get_inport_message(aml_fast_mixer_inport_st *port)
|
|
{
|
|
aml_fast_mixer_inport_message_t *p_msg = NULL;
|
|
struct listnode *item = NULL;
|
|
|
|
pthread_mutex_lock(&port->msg_lock);
|
|
if (!list_empty(&port->msg_list)) {
|
|
item = list_head(&port->msg_list);
|
|
p_msg = node_to_item(item, aml_fast_mixer_inport_message_t, list);
|
|
}
|
|
pthread_mutex_unlock(&port->msg_lock);
|
|
return p_msg;
|
|
}
|
|
|
|
static bool fast_mixer_inport_message_empty(aml_fast_mixer_inport_st *port)
|
|
{
|
|
bool empty = true;
|
|
|
|
pthread_mutex_lock(&port->msg_lock);
|
|
if (!list_empty(&port->msg_list)) {
|
|
empty = false;
|
|
}
|
|
pthread_mutex_unlock(&port->msg_lock);
|
|
return empty;
|
|
}
|
|
|
|
static int fast_mixer_remove_inport_message(aml_fast_mixer_inport_st *port, aml_fast_mixer_inport_message_t *p_msg)
|
|
{
|
|
if (port == NULL || p_msg == NULL) {
|
|
return 0;
|
|
}
|
|
pthread_mutex_lock(&port->msg_lock);
|
|
list_remove(&p_msg->list);
|
|
pthread_mutex_unlock(&port->msg_lock);
|
|
aml_audio_free(p_msg);
|
|
return 0;
|
|
}
|
|
|
|
static int fast_mixer_remove_inport_all_messages(aml_fast_mixer_inport_st *port)
|
|
{
|
|
aml_fast_mixer_inport_message_t *p_msg = NULL;
|
|
struct listnode *node = NULL, *n = NULL;
|
|
|
|
pthread_mutex_lock(&port->msg_lock);
|
|
list_for_each_safe(node, n, &port->msg_list) {
|
|
p_msg = node_to_item(node, aml_fast_mixer_inport_message_t, list);
|
|
AM_LOGI("msg: %s", fast_mixer_inport_msg_to_str(p_msg->msg_what));
|
|
list_remove(&p_msg->list);
|
|
aml_audio_free(p_msg);
|
|
}
|
|
pthread_mutex_unlock(&port->msg_lock);
|
|
return 0;
|
|
}
|
|
|
|
static void fast_mixer_process_inport_msg(aml_fast_mixer_inport_st *in_port)
|
|
{
|
|
aml_fast_mixer_inport_message_t *msg = fast_mixer_get_inport_message(in_port);
|
|
if (msg) {
|
|
AM_LOGI("msg: %s", fast_mixer_inport_msg_to_str(msg->msg_what));
|
|
switch (msg->msg_what) {
|
|
case AML_FAST_MIXER_MSG_PAUSE:
|
|
in_port->state = AML_FAST_MIXER_INPORT_PAUSING;
|
|
break;
|
|
case AML_FAST_MIXER_MSG_RESUME:
|
|
in_port->state = AML_FAST_MIXER_INPORT_RESUMING;
|
|
break;
|
|
case AML_FAST_MIXER_MSG_FLUSH:
|
|
in_port->state = AML_FAST_MIXER_INPORT_FLUSHING;
|
|
break;
|
|
case AML_FAST_MIXER_MSG_STOP:
|
|
in_port->state = AML_FAST_MIXER_INPORT_STOPPING;
|
|
break;
|
|
case AML_FAST_MIXER_MSG_STANDBY:
|
|
in_port->state = AML_FAST_MIXER_INPORT_STANDBYING;
|
|
break;
|
|
default:
|
|
AM_LOGE("not support %d", msg->msg_what);
|
|
break;
|
|
}
|
|
fast_mixer_remove_inport_message(in_port, msg);
|
|
}
|
|
}
|
|
|
|
void aml_fast_mixer_inport_set_mute(void *fast_mixer, int port_id, bool mute)
|
|
{
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return;
|
|
}
|
|
if (port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("invalid port_id %d", port_id);
|
|
return;
|
|
}
|
|
|
|
input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
AM_LOGE("port_id %d, input port is NULL", port_id);
|
|
return;
|
|
}
|
|
input_port->mute = mute;
|
|
|
|
// set hw mixer state by pcm api
|
|
if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
float src_vol = 0;
|
|
if (mute) {
|
|
src_vol = 0;
|
|
} else {
|
|
src_vol = input_port->volume;
|
|
}
|
|
// set src volume to hwmixer handle
|
|
aml_hwmixer_handle_config(&input_port->hwmixer_hdl, AML_HWMIXER_CFG_TYPE_VOLUME_SET, &src_vol);
|
|
}
|
|
AM_LOGI("%s port_id %d, mute %d", mixer->name, port_id, mute);
|
|
}
|
|
|
|
|
|
void aml_fast_mixer_inport_set_volume(void *fast_mixer, int port_id, float volume)
|
|
{
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return;
|
|
}
|
|
if (port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("invalid port_id %d", port_id);
|
|
return;
|
|
}
|
|
|
|
input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
AM_LOGE("port_id %d, input port is NULL", port_id);
|
|
return;
|
|
}
|
|
input_port->volume = volume;
|
|
|
|
// set hw mixer state by pcm api
|
|
if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
float src_vol = 0;
|
|
if (input_port->mute) {
|
|
src_vol = 0;
|
|
} else {
|
|
src_vol = volume;
|
|
}
|
|
// set src volume to hwmixer handle
|
|
aml_hwmixer_handle_config(&input_port->hwmixer_hdl, AML_HWMIXER_CFG_TYPE_VOLUME_SET, &src_vol);
|
|
}
|
|
AM_LOGI("%s port_id %d, volume %f", mixer->name, port_id, volume);
|
|
}
|
|
|
|
|
|
int aml_fast_mixer_inport_get_position(void *fast_mixer, int port_id, uint64_t *frames, struct timespec *timestamp)
|
|
{
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
if (port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM || frames == NULL || timestamp == NULL) {
|
|
AM_LOGE("invalid parameter");
|
|
return -1;
|
|
}
|
|
|
|
input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
AM_LOGE("input_port is NULL");
|
|
return -1;
|
|
}
|
|
|
|
/* HW noirq get position and timestamp from alsa */
|
|
if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
int ret = 0;
|
|
unsigned int hw_ptr = 0;
|
|
struct timespec hw_timestamp = {0, 0};
|
|
/* get hw ptr from pcm_mmap_get_hw_ptr */
|
|
ret = aml_hwmixer_pcm_mmap_get_hw_ptr(&input_port->hwmixer_hdl, &hw_ptr, &hw_timestamp);
|
|
if (ret < 0) {
|
|
AM_LOGE("pcm_mmap_get_hw_ptr err");
|
|
return ret;
|
|
}
|
|
pthread_mutex_lock(&input_port->apts_lock);
|
|
*frames = (uint64_t)hw_ptr;
|
|
*timestamp = hw_timestamp;
|
|
pthread_mutex_unlock(&input_port->apts_lock);
|
|
} else {
|
|
/* SW and HW with irq get position and timestamp info from input port */
|
|
pthread_mutex_lock(&input_port->apts_lock);
|
|
*frames = input_port->last_frames_position + input_port->drop_frames;
|
|
*timestamp = input_port->lasttimestamp;
|
|
pthread_mutex_unlock(&input_port->apts_lock);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* get pcm info when use hw mixer */
|
|
int aml_fast_mixer_inport_get_pcm_info(void *fast_mixer, int port_id,
|
|
struct pcm *pcm, struct pcm_config *pcm_cfg)
|
|
{
|
|
int ret = -1;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
struct pcm *temp_pcm = NULL;
|
|
struct pcm_config *temp_cfg = NULL;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
if (port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("invalid parameter");
|
|
return -1;
|
|
}
|
|
|
|
aml_fast_mixer_inport_st *input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
AM_LOGE("input_port is NULL");
|
|
return -1;
|
|
}
|
|
|
|
if (input_port->use_hw_mixer) {
|
|
temp_pcm = input_port->hwmixer_hdl.param.pcm_handle;
|
|
temp_cfg = &input_port->hwmixer_hdl.param.mixer_pcm_config;
|
|
memcpy(pcm_cfg, temp_cfg, sizeof(struct pcm_config));
|
|
pcm = temp_pcm;
|
|
ret = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* pcm operations for hwmixer or other alsa card */
|
|
int aml_fast_mixer_inport_pcm_func(void *fast_mixer, int port_id,
|
|
aml_fast_mixer_pcm_func_t pcm_func,
|
|
aml_fast_mixer_pcm_data_st *pcm_data)
|
|
{
|
|
int ret = -1;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
if (port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("invalid parameter");
|
|
return -1;
|
|
}
|
|
aml_fast_mixer_inport_st *input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
AM_LOGE("input_port is NULL");
|
|
return -1;
|
|
}
|
|
/* only for hw mxier case */
|
|
if (!input_port->use_hw_mixer) {
|
|
ret = -1;
|
|
}
|
|
|
|
AM_LOGD("port_id = %d, pcm_func = %d", port_id, pcm_func);
|
|
|
|
/* pcm func without data */
|
|
switch (pcm_func) {
|
|
case AML_FAST_MIXER_PCM_GET_POLL_FD:
|
|
ret = aml_hwmixer_pcm_get_poll_fd(&input_port->hwmixer_hdl);
|
|
goto exit;
|
|
case AML_FAST_MIXER_PCM_GET_BUFFER_SIZE:
|
|
ret = aml_hwmixer_pcm_get_buffer_size(&input_port->hwmixer_hdl);
|
|
goto exit;
|
|
default:
|
|
ret = -1;
|
|
}
|
|
|
|
/* pcm func with data */
|
|
if (!pcm_data) {
|
|
AM_LOGE("pcm_data is NULL");
|
|
return -1;
|
|
}
|
|
switch (pcm_func) {
|
|
case AML_FAST_MIXER_PCM_MMAP_BEGIN:
|
|
ret = aml_hwmixer_pcm_mmap_begin(&input_port->hwmixer_hdl, &pcm_data->pcm_areas,
|
|
&pcm_data->pcm_offset, &pcm_data->frames);
|
|
break;
|
|
case AML_FAST_MIXER_PCM_MMAP_COMMIT:
|
|
ret = aml_hwmixer_pcm_mmap_commit(&input_port->hwmixer_hdl, pcm_data->frames);
|
|
break;
|
|
case AML_FAST_MIXER_PCM_MMAP_GET_HW_PTR:
|
|
ret = aml_hwmixer_pcm_mmap_get_hw_ptr(&input_port->hwmixer_hdl, &pcm_data->pcm_offset, &pcm_data->tstamp);
|
|
break;
|
|
case AML_FAST_MIXER_PCM_FRAMES_TO_BYTES:
|
|
ret = aml_hwmixer_pcm_frames_to_bytes(&input_port->hwmixer_hdl, pcm_data->frames);
|
|
break;
|
|
default:
|
|
AM_LOGD("not supported id %d", pcm_func);
|
|
ret = -1;
|
|
}
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
|
|
int aml_fast_mixer_inport_avail_frames(void *fast_mixer, int port_id)
|
|
{
|
|
int avail_frames = 0;
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
if (port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("invalid parameter");
|
|
return -1;
|
|
}
|
|
|
|
input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
AM_LOGE("input_port is NULL");
|
|
return -1;
|
|
}
|
|
|
|
if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_SW) {
|
|
avail_frames = get_buffer_read_space(&input_port->buffer.rbuffer) / input_port->param.config.frame_size;
|
|
} else if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW ||
|
|
input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
avail_frames = aml_hwmixer_get_avail_frames(&input_port->hwmixer_hdl);
|
|
}
|
|
|
|
return avail_frames;
|
|
}
|
|
|
|
int aml_fast_mixer_inport_write_space(void *fast_mixer, int port_id)
|
|
{
|
|
int writable_bytes = 0;
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGV("mixer is NULL or not initialized");
|
|
return 0;
|
|
}
|
|
if (port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("invalid parameter");
|
|
return 0;
|
|
}
|
|
|
|
input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
AM_LOGE("input_port is NULL");
|
|
return 0;
|
|
}
|
|
|
|
if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_SW) {
|
|
writable_bytes = get_buffer_write_space(&input_port->buffer.rbuffer);
|
|
} else if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW ||
|
|
input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
// todo
|
|
}
|
|
return writable_bytes;
|
|
}
|
|
|
|
|
|
int aml_fast_mixer_inport_write(void *fast_mixer, int port_id, const void *buffer, int bytes)
|
|
{
|
|
int ret = 0;
|
|
int avail_bytes = 0;
|
|
int frame_size = 0;
|
|
const struct audioCfg *input_cfg = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
bool dump_enable = get_debug_value(AML_DUMP_AUDIOHAL_FAST_MIXER);
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return 0;
|
|
}
|
|
if (buffer == NULL || bytes <= 0 || port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("invalid param, buffer %p, bytes %d, port_id %d", buffer, bytes, port_id);
|
|
return 0;
|
|
}
|
|
|
|
aml_fast_mixer_inport_st *input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
AM_LOGE("input_port is NULL");
|
|
return 0;
|
|
}
|
|
input_cfg = &input_port->param.config;
|
|
frame_size = input_cfg->frame_size;
|
|
|
|
if (dump_enable) {
|
|
char filepath[64];
|
|
memset(filepath, 0, sizeof(filepath));
|
|
snprintf(filepath, sizeof(filepath)-1, "/data/vendor/audiohal/%s_in_%d_write.pcm", mixer->name, input_port->port_id);
|
|
aml_dump_audio_bitstreams(filepath, buffer, bytes);
|
|
}
|
|
|
|
if (mixer->drop_enable) {
|
|
avail_bytes = get_buffer_read_space(&input_port->buffer.rbuffer);
|
|
if (avail_bytes > 0) {
|
|
ring_buffer_reset(&input_port->buffer.rbuffer);
|
|
AM_LOGI("%s drop enable, drop ringbuffer %d frames", mixer->name, avail_bytes / frame_size);
|
|
}
|
|
input_port->first_read = true;
|
|
|
|
pthread_mutex_lock(&input_port->apts_lock);
|
|
input_port->drop_frames += ((bytes + avail_bytes) / frame_size);
|
|
clock_gettime(CLOCK_MONOTONIC, &input_port->lasttimestamp);
|
|
pthread_mutex_unlock(&input_port->apts_lock);
|
|
AM_LOGI("%s drop enable, drop %d frames", mixer->name, bytes / frame_size);
|
|
return 0;
|
|
}
|
|
|
|
if (input_port->first_read && input_port->param.estimate_buffer_level) {
|
|
input_port->write_time_us = (int64_t)aml_audio_get_systime();
|
|
}
|
|
|
|
if (input_port->param.read_callback) {
|
|
// nothing to do ?
|
|
|
|
} else if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_SW) {
|
|
// write data to ring buffer
|
|
avail_bytes = get_buffer_write_space(&input_port->buffer.rbuffer);
|
|
if (avail_bytes < bytes) {
|
|
AM_LOGE("%s port_id %d avail_bytes %d < bytes %d, override data !", mixer->name, port_id, avail_bytes, bytes);
|
|
}
|
|
ret = ring_buffer_write(&input_port->buffer.rbuffer, (void *)buffer, bytes, COVER_WRITE);
|
|
} else if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW) {
|
|
// write data to hwmixer and do process inside
|
|
if (aml_hwmixer_handle_write(&input_port->hwmixer_hdl, (void *)buffer, bytes)) {
|
|
AM_LOGE("aml_hwmixer_handle_write error!");
|
|
ret = 0;
|
|
}
|
|
// update position and timestamp
|
|
pthread_mutex_lock(&input_port->apts_lock);
|
|
input_port->last_frames_position += bytes/frame_size;
|
|
clock_gettime(CLOCK_MONOTONIC, &input_port->lasttimestamp);
|
|
pthread_mutex_unlock(&input_port->apts_lock);
|
|
} else if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
// nothing to do
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline void _mixer_inport_invalid(aml_fast_mixer_inport_st *input_port)
|
|
{
|
|
aml_pcm_downmix_st *p_downmixer = NULL;
|
|
fast_mixer_format_buffer_t *p_format_buf = NULL;
|
|
|
|
if (input_port->data_bufptr && input_port->data_buflen > 0 && input_port->data_valid) {
|
|
memset(input_port->data_bufptr, 0, input_port->data_buflen);
|
|
}
|
|
|
|
p_downmixer = input_port->buffer.downmixer;
|
|
if (p_downmixer && p_downmixer->output_buf && p_downmixer->output_buf_size > 0) {
|
|
memset(p_downmixer->output_buf, 0, p_downmixer->output_buf_size);
|
|
}
|
|
|
|
p_format_buf = &input_port->buffer.format_buf;
|
|
if (p_format_buf->buf_ptr && p_format_buf->buf_size > 0 && p_format_buf->valid_samples > 0) {
|
|
memset(p_format_buf->buf_ptr, 0, p_format_buf->buf_size);
|
|
}
|
|
p_format_buf->valid_samples = 0;
|
|
p_format_buf->format = AUDIO_FORMAT_INVALID;
|
|
|
|
input_port->data_frames = 0;
|
|
input_port->data_valid = false;
|
|
input_port->data_planar_buf.data_valid = false;
|
|
input_port->data_planar_buf.format = AUDIO_FORMAT_INVALID;
|
|
}
|
|
|
|
static int _aml_fast_mixer_inport_reset(aml_fast_mixer_inport_st *input_port)
|
|
{
|
|
int avail_frames = 0;
|
|
aml_fast_mixer_buffer_st *mix_buffer = NULL;
|
|
|
|
if (input_port == NULL) {
|
|
return -1;
|
|
}
|
|
mix_buffer = &input_port->buffer;
|
|
|
|
pthread_mutex_lock(&input_port->apts_lock);
|
|
input_port->drop_frames = 0;
|
|
input_port->last_frames_position = 0;
|
|
memset(&input_port->lasttimestamp, 0, sizeof(input_port->lasttimestamp));
|
|
pthread_mutex_unlock(&input_port->apts_lock);
|
|
|
|
if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_SW) {
|
|
if (input_port->param.buffer_frame_count > 0) {
|
|
avail_frames = get_buffer_read_space(&mix_buffer->rbuffer) / input_port->param.config.frame_size;
|
|
AM_LOGI("port %d %s drop %d frames", input_port->port_id, input_port->param.desc_info, avail_frames);
|
|
ring_buffer_reset(&mix_buffer->rbuffer);
|
|
}
|
|
memset(&mix_buffer->resample_config, 0, sizeof(mix_buffer->resample_config));
|
|
if (mix_buffer->resample_handle) {
|
|
aml_audio_resample_close(mix_buffer->resample_handle);
|
|
mix_buffer->resample_handle = NULL;
|
|
}
|
|
if (mix_buffer->downmixer) {
|
|
deinit_aml_pcm_downmix(mix_buffer->downmixer);
|
|
aml_audio_free(mix_buffer->downmixer);
|
|
mix_buffer->downmixer = NULL;
|
|
}
|
|
} else if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW) {
|
|
if (aml_hwmixer_handle_reset(&input_port->hwmixer_hdl)) {
|
|
AM_LOGE("aml_hwmixer_handle_reset error");
|
|
}
|
|
} else if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
// nothing todo
|
|
}
|
|
|
|
input_port->state = AML_FAST_MIXER_INPORT_IDLE;
|
|
input_port->first_read = true;
|
|
return 0;
|
|
}
|
|
|
|
int aml_fast_mixer_inport_flush(void *fast_mixer, int port_id)
|
|
{
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
if (port_id < 0 || port_id >= AML_FAST_MIXER_INPORT_NUM) {
|
|
AM_LOGE("invalid parameter");
|
|
return -EINVAL;
|
|
}
|
|
|
|
aml_fast_mixer_inport_st *input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
AM_LOGE("input_port is NULL");
|
|
return -EINVAL;
|
|
}
|
|
|
|
pthread_mutex_lock(&mixer->lock);
|
|
input_port->state = AML_FAST_MIXER_INPORT_IDLE;
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
|
|
_aml_fast_mixer_inport_reset(input_port);
|
|
return 0;
|
|
}
|
|
|
|
int aml_fast_mixer_all_inports_flush(void *fast_mixer)
|
|
{
|
|
int port_id = 0;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
|
|
for (port_id = 0; port_id < AML_FAST_MIXER_INPORT_NUM; port_id++) {
|
|
aml_fast_mixer_inport_st *input_port = mixer->inports[port_id];
|
|
if (input_port == NULL) {
|
|
continue;
|
|
}
|
|
pthread_mutex_lock(&mixer->lock);
|
|
input_port->state = AML_FAST_MIXER_INPORT_IDLE;
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
|
|
_aml_fast_mixer_inport_reset(input_port);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
static int aml_fast_mixer_inport_resample(aml_fast_mixer_buffer_st *mix_buffer,
|
|
void *in_buffer, int in_bytes, const struct audioCfg *in_cfg, const struct audioCfg *mix_cfg)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* init resample config */
|
|
if (in_cfg->sampleRate != mix_buffer->resample_config.output_sr) {
|
|
if (mix_buffer->resample_handle) {
|
|
aml_audio_resample_close(mix_buffer->resample_handle);
|
|
mix_buffer->resample_handle = NULL;
|
|
}
|
|
|
|
mix_buffer->resample_config.aformat = in_cfg->format;
|
|
mix_buffer->resample_config.channels = in_cfg->channelCnt;
|
|
mix_buffer->resample_config.input_sr = in_cfg->sampleRate;
|
|
mix_buffer->resample_config.output_sr = mix_cfg->sampleRate;
|
|
AM_LOGI("init resampler from in-samplerate(%d) to out-samplerate(%d) channels(%d) format(%d)\n",
|
|
mix_buffer->resample_config.input_sr, mix_buffer->resample_config.output_sr,
|
|
mix_buffer->resample_config.channels, mix_buffer->resample_config.aformat);
|
|
ret = aml_audio_resample_init((aml_audio_resample_t **)&mix_buffer->resample_handle,
|
|
AML_AUDIO_SIMPLE_RESAMPLE, &mix_buffer->resample_config);
|
|
if (ret < 0) {
|
|
AM_LOGE("resample init error\n");
|
|
mix_buffer->resample_handle = NULL;
|
|
return -1;
|
|
}
|
|
} else {
|
|
mix_buffer->resample_handle = NULL;
|
|
}
|
|
|
|
if (mix_buffer->resample_handle) {
|
|
if (aml_audio_resample_process(mix_buffer->resample_handle, in_buffer, in_bytes) != 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void fast_mixer_config_fading(aml_fast_mixer_inport_st *in_port, bool is_fade_in, int avail_frames, const struct audioCfg *data_cfg)
|
|
{
|
|
ease_setting_t setting;
|
|
aml_data_format_t data_format;
|
|
int ease_frames = 512;
|
|
float current_volume = 1.0f;
|
|
|
|
if (ease_frames > avail_frames && !is_fade_in) {
|
|
AM_LOGI("fade out, avail_frames %d", avail_frames);
|
|
ease_frames = avail_frames;
|
|
}
|
|
data_format.format = data_cfg->format;
|
|
data_format.sr = data_cfg->sampleRate;
|
|
data_format.ch = data_cfg->channelCnt;
|
|
data_format.endian = 0;
|
|
memset(&setting, 0, sizeof(setting));
|
|
if (in_port->fade_ease == NULL) {
|
|
if (aml_audio_ease_init(&in_port->fade_ease) != 0) {
|
|
AM_LOGE("init fade_ease fail !");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (is_fade_in) {
|
|
setting.start_volume = 0;
|
|
setting.target_volume = 1.0;
|
|
} else {
|
|
setting.start_volume = 1.0;
|
|
setting.target_volume = 0;
|
|
}
|
|
|
|
if (in_port->fade_ease && in_port->fade_ease->do_easing) {
|
|
current_volume = aml_audio_ease_get_current_volume(in_port->fade_ease);
|
|
setting.start_volume = current_volume;
|
|
AM_LOGI("input port %d do_easing, start_volume %f", in_port->port_id, setting.start_volume);
|
|
}
|
|
AM_LOGI("fade %s, easing frames %d", is_fade_in ? "in" : "out", ease_frames);
|
|
|
|
setting.ease_type = EaseLinear;
|
|
setting.ease_frames = ease_frames;
|
|
aml_audio_ease_config_frame(in_port->fade_ease, &setting, &data_format);
|
|
}
|
|
|
|
static int _mixer_inport_volume_mute_process(aml_fast_mixer_inport_st *input_port, void *buffer, int bytes, const struct audioCfg *data_cfg)
|
|
{
|
|
float next_volume = 1.0f;
|
|
float current_volume = 1.0f;
|
|
float target_volume = 1.0f;
|
|
ease_setting_t ease_setting;
|
|
aml_data_format_t data_format;
|
|
aml_audio_ease_t *p_volume_ease = NULL;
|
|
int debug_value = get_debug_value(AML_DEBUG_AUDIOHAL_FAST_MIXER);
|
|
|
|
if (input_port == NULL || buffer == NULL || bytes <= 0 || data_cfg == NULL) {
|
|
AM_LOGE("invalid, input_port(%p) buffer(%p) bytes(%d) data_cfg(%p)", input_port, buffer, bytes, data_cfg);
|
|
return -1;
|
|
}
|
|
p_volume_ease = input_port->vol_ease;
|
|
next_volume = input_port->volume;
|
|
if (input_port->mute) {
|
|
next_volume = 0;
|
|
}
|
|
current_volume = p_volume_ease->current_volume;
|
|
target_volume = p_volume_ease->target_volume;
|
|
|
|
if (!is_float_equal(target_volume, next_volume) || !p_volume_ease->do_easing) {
|
|
memset(&ease_setting, 0, sizeof(ease_setting));
|
|
ease_setting.ease_type = EaseLinear;
|
|
ease_setting.ease_frames = 384;
|
|
ease_setting.start_volume = current_volume;
|
|
ease_setting.target_volume = next_volume;
|
|
|
|
data_format.format = data_cfg->format;
|
|
data_format.sr = data_cfg->sampleRate;
|
|
data_format.ch = data_cfg->channelCnt;
|
|
data_format.endian = 0;
|
|
aml_audio_ease_config_frame(p_volume_ease, &ease_setting, &data_format);
|
|
}
|
|
|
|
bool skip_easing = false;
|
|
if (is_float_equal(p_volume_ease->start_volume, p_volume_ease->target_volume) || aml_audio_ease_done(p_volume_ease)) {
|
|
if (is_float_equal(p_volume_ease->target_volume, 1.0f)) {
|
|
skip_easing = true;
|
|
if (debug_value) {
|
|
AM_LOGD("port %d volume is 1.0f, skip easing !", input_port->port_id);
|
|
}
|
|
} else if (is_float_equal(p_volume_ease->target_volume, 0.0f)) {
|
|
skip_easing = true;
|
|
memset(buffer, 0, bytes);
|
|
}
|
|
}
|
|
|
|
if (skip_easing) {
|
|
p_volume_ease->ease_frames_elapsed = p_volume_ease->ease_frames;
|
|
p_volume_ease->do_easing = false;
|
|
} else {
|
|
aml_audio_ease_process(input_port->vol_ease, buffer, bytes);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int _mixer_inport_read(aml_fast_mixer_st *mixer, int port_id, int req_frames)
|
|
{
|
|
int ret = 0;
|
|
int read_bytes = 0;
|
|
int read_frames = 0;
|
|
int avail_frames = 0;
|
|
int64_t estimated_frames = 0;
|
|
int request_bytes = 0;
|
|
bool config_fade_out = false;
|
|
bool config_fade_in = false;
|
|
bool dump_enable = get_debug_value(AML_DUMP_AUDIOHAL_FAST_MIXER);
|
|
bool debug_enable = get_debug_value(AML_DEBUG_AUDIOHAL_FAST_MIXER);
|
|
const struct audioCfg *input_cfg = NULL;
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_buffer_st *mix_buffer = NULL;
|
|
aml_fast_mixer_inport_param_st *input_param = NULL;
|
|
|
|
input_port = mixer->inports[port_id];
|
|
input_param = &input_port->param;
|
|
input_cfg = &input_param->config;
|
|
mix_buffer = &input_port->buffer;
|
|
request_bytes = req_frames * input_cfg->frame_size;
|
|
|
|
ret = aml_audio_check_and_realloc((void **)&input_port->data_bufptr, &input_port->data_buflen, request_bytes);
|
|
if ((ret != 0) || (input_port->data_bufptr == NULL)) {
|
|
AM_LOGE("allocate input_port %d data_bufptr(%d bytes) failed", input_port->port_id, request_bytes);
|
|
goto read_end;
|
|
}
|
|
|
|
// process input port message
|
|
fast_mixer_process_inport_msg(input_port);
|
|
|
|
if (input_port->state == AML_FAST_MIXER_INPORT_PAUSING) {
|
|
input_port->state = AML_FAST_MIXER_INPORT_PAUSING_FADE;
|
|
config_fade_out = true;
|
|
} else if (input_port->state == AML_FAST_MIXER_INPORT_PAUSING_FADE) {
|
|
if ((input_port && input_port->fade_ease == NULL)
|
|
|| (input_port && input_port->fade_ease && !input_port->fade_ease->do_easing)) {
|
|
input_port->state = AML_FAST_MIXER_INPORT_PAUSED;
|
|
}
|
|
} else if (input_port->state == AML_FAST_MIXER_INPORT_RESUMING) {
|
|
input_port->state = AML_FAST_MIXER_INPORT_ACTIVE;
|
|
} else if (input_port->state == AML_FAST_MIXER_INPORT_FLUSHING) {
|
|
_aml_fast_mixer_inport_reset(input_port);
|
|
input_port->state = AML_FAST_MIXER_INPORT_FLUSHED;
|
|
} else if (input_port->state == AML_FAST_MIXER_INPORT_STOPPING) {
|
|
_aml_fast_mixer_inport_reset(input_port);
|
|
input_port->state = AML_FAST_MIXER_INPORT_STOPPED;
|
|
} else if (input_port->state == AML_FAST_MIXER_INPORT_STANDBYING) {
|
|
if (input_port->param.mixer_type == AML_FAST_MIX_TYPE_SW && input_param->read_callback == NULL) {
|
|
int remaining_bytes = get_buffer_read_space(&input_port->buffer.rbuffer);
|
|
if (remaining_bytes > 0) {
|
|
if (input_port->standby_info_show == false) {
|
|
AM_LOGI("%s port_id %d prepare to standby, remaining_bytes %d, req_frames %d", mixer->name, port_id, remaining_bytes, req_frames);
|
|
}
|
|
input_port->standby_info_show = true;
|
|
} else {
|
|
AM_LOGI("%s port_id %d is standby", mixer->name, port_id);
|
|
input_port->state = AML_FAST_MIXER_INPORT_STANDBYED;
|
|
input_port->standby_info_show = false;
|
|
}
|
|
} else {
|
|
// hw mixer do some operation? a todo list
|
|
input_port->state = AML_FAST_MIXER_INPORT_STANDBYED;
|
|
AM_LOGI("%s port_id %d is standby", mixer->name, port_id);
|
|
}
|
|
}
|
|
|
|
if (input_port->state == AML_FAST_MIXER_INPORT_PAUSED
|
|
|| input_port->state == AML_FAST_MIXER_INPORT_FLUSHED
|
|
|| input_port->state == AML_FAST_MIXER_INPORT_STOPPED
|
|
|| input_port->state == AML_FAST_MIXER_INPORT_STANDBYED) {
|
|
// prepare for the next reading
|
|
input_port->first_read = true;
|
|
return 0;
|
|
}
|
|
if (get_debug_value(AML_DEBUG_AUDIOHAL_FAST_MIXER)) {
|
|
if (input_port->data_valid) {
|
|
AM_LOGW("%s port_id %d already has valid data, override !", mixer->name, port_id);
|
|
}
|
|
}
|
|
// read data
|
|
if (input_param->read_callback) {
|
|
read_bytes = input_param->read_callback(input_param->callback_data, mixer->drop_enable, input_port->data_bufptr, request_bytes);
|
|
} else if (input_param->mixer_type == AML_FAST_MIX_TYPE_SW) {
|
|
if (input_port->first_read) {
|
|
avail_frames = get_buffer_read_space(&mix_buffer->rbuffer) / input_cfg->frame_size;
|
|
if (input_port->param.estimate_buffer_level && avail_frames > 0) {
|
|
int64_t curr_time_us = (int64_t)aml_audio_get_systime();
|
|
if (curr_time_us > input_port->write_time_us) {
|
|
int64_t diff_time_us = curr_time_us - input_port->write_time_us;
|
|
estimated_frames = diff_time_us * input_cfg->sampleRate / 1000000L;
|
|
if (debug_enable) {
|
|
AM_LOGI("%s %s port %d, diff_time_us %" PRId64 ", sampleRate %d",
|
|
__func__, mixer->name, port_id, diff_time_us, input_cfg->sampleRate);
|
|
}
|
|
|
|
if (estimated_frames > input_port->param.buffer_frame_count/2) {
|
|
AM_LOGE("%s %s port %d, invalid estimated_frames %" PRId64 " (discard it), buffer_frame_count %d",
|
|
__func__, mixer->name, port_id, estimated_frames, input_port->param.buffer_frame_count);
|
|
estimated_frames = 0;
|
|
}
|
|
}
|
|
AM_LOGI("%s %s port %d, avail_frames %d, estimated_frames %" PRId64 "",
|
|
__func__, mixer->name, port_id, avail_frames, estimated_frames);
|
|
avail_frames += estimated_frames;
|
|
}
|
|
|
|
if (avail_frames < input_port->param.start_threshold_frame) {
|
|
goto read_end;
|
|
}
|
|
input_port->first_read = false;
|
|
config_fade_in = true;
|
|
}
|
|
if (get_debug_value(AML_DEBUG_AUDIOHAL_FAST_MIXER)) {
|
|
avail_frames = get_buffer_read_space(&mix_buffer->rbuffer) / input_cfg->frame_size;
|
|
AM_LOGI("%s port %d, avail_frames %d, latency %d ms", mixer->name, port_id, avail_frames,
|
|
avail_frames * 1000 / input_cfg->sampleRate);
|
|
}
|
|
|
|
read_bytes = ring_buffer_read(&mix_buffer->rbuffer, input_port->data_bufptr, request_bytes);
|
|
// HW Mixer do not need read because data already write to hwmixer in aml_fast_mixer_inport_write
|
|
} else if (input_param->mixer_type == AML_FAST_MIX_TYPE_HW ||
|
|
input_param->mixer_type == AML_FAST_MIX_TYPE_HW_NOIRQ) {
|
|
read_frames = 0;
|
|
goto read_end;
|
|
}
|
|
|
|
if (read_bytes <= 0) {
|
|
AM_LOGW("%s port %d without data", mixer->name, port_id);
|
|
goto read_end;
|
|
} else if (read_bytes < request_bytes) {
|
|
AM_LOGW("%s port %d not enough data (%d < %d)", mixer->name, port_id, read_bytes, request_bytes);
|
|
}
|
|
|
|
if (dump_enable) {
|
|
char filepath[64];
|
|
memset(filepath, 0, sizeof(filepath));
|
|
snprintf(filepath, sizeof(filepath)-1, "/data/vendor/audiohal/%s_in_%d_read.pcm", mixer->name, input_port->port_id);
|
|
aml_dump_audio_bitstreams(filepath, input_port->data_bufptr, read_bytes);
|
|
}
|
|
|
|
read_frames = read_bytes / input_cfg->frame_size;
|
|
_mixer_inport_volume_mute_process(input_port, input_port->data_bufptr, read_bytes, input_cfg);
|
|
|
|
// fade in/out process
|
|
if (!input_param->disable_fading) {
|
|
if (config_fade_in || config_fade_out) {
|
|
fast_mixer_config_fading(input_port, config_fade_in, avail_frames, input_cfg);
|
|
}
|
|
if (input_port->fade_ease && input_port->fade_ease->do_easing && read_bytes > 0) {
|
|
aml_audio_ease_process(input_port->fade_ease, input_port->data_bufptr, read_bytes);
|
|
}
|
|
}
|
|
|
|
if (read_frames > 0) {
|
|
input_port->data_frames = read_frames;
|
|
input_port->data_valid = true;
|
|
}
|
|
pthread_mutex_lock(&input_port->apts_lock);
|
|
input_port->last_frames_position += read_frames;
|
|
clock_gettime(CLOCK_MONOTONIC, &input_port->lasttimestamp);
|
|
pthread_mutex_unlock(&input_port->apts_lock);
|
|
|
|
read_end:
|
|
return read_frames;
|
|
}
|
|
|
|
static int _mixer_inport_process(aml_fast_mixer_st *p_mixer, int port_id, int req_frames,
|
|
void **p_out_buffer, struct audioCfg *p_out_cfg, const struct audioCfg *p_mix_cfg)
|
|
{
|
|
int ret = 0;
|
|
int read_frames = 0;
|
|
int read_samples = 0;
|
|
const struct audioCfg *input_cfg = NULL;
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_buffer_st *mix_buffer = NULL;
|
|
aml_fast_mixer_inport_param_st *input_param = NULL;
|
|
fast_mixer_format_buffer_t *p_format_buf = NULL;
|
|
void *out_buffer = NULL;
|
|
struct audioCfg out_cfg;
|
|
|
|
input_port = p_mixer->inports[port_id];
|
|
input_param = &input_port->param;
|
|
input_cfg = &input_param->config;
|
|
mix_buffer = &input_port->buffer;
|
|
|
|
if (input_port->data_valid == false) {
|
|
AM_LOGI("data_valid is false");
|
|
return 0;
|
|
}
|
|
if (input_port->data_frames < req_frames) {
|
|
AM_LOGW("%s port %d data_frames(%d) < req_frames(%d)", p_mixer->name, port_id, input_port->data_frames, req_frames);
|
|
}
|
|
|
|
memcpy(&out_cfg, input_cfg, sizeof(out_cfg));
|
|
out_buffer = input_port->data_bufptr;
|
|
read_frames = input_port->data_frames;
|
|
|
|
// do downmix
|
|
if (out_cfg.channelCnt > 2 && out_cfg.channelCnt > p_mix_cfg->channelCnt) {
|
|
if (mix_buffer->downmixer == NULL) {
|
|
mix_buffer->downmixer = aml_audio_malloc(sizeof(*mix_buffer->downmixer));
|
|
if (mix_buffer->downmixer) {
|
|
init_aml_pcm_downmix(mix_buffer->downmixer);
|
|
} else {
|
|
AM_LOGE("malloc %zu bytes fail !", sizeof(*mix_buffer->downmixer));
|
|
}
|
|
}
|
|
if (mix_buffer->downmixer) {
|
|
if (do_downmix_to_2ch(mix_buffer->downmixer, out_buffer, read_frames, &out_cfg) >= 0) {
|
|
out_cfg.channelCnt = 2;
|
|
out_cfg.channelMask = AUDIO_CHANNEL_OUT_STEREO;
|
|
out_buffer = mix_buffer->downmixer->output_buf;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* do resample */
|
|
/* need define start_threshold to cache buffer cause length of resample output each time might change*/
|
|
if (out_cfg.sampleRate != p_mix_cfg->sampleRate) {
|
|
// 1. fast inport should not call resample function.
|
|
// 2. this function may be called multi times(one source mixed into multi output), then resample work abnormally.
|
|
#if 0
|
|
if (aml_fast_mixer_inport_resample(mix_buffer, out_buffer, read_bytes, &out_cfg, mix_cfg) == 0) {
|
|
out_cfg.sampleRate = p_mix_cfg->sampleRate;
|
|
out_buffer = mix_buffer->resample_handle->resample_buffer;
|
|
read_bytes = mix_buffer->resample_handle->resample_size;
|
|
read_frames = mix_buffer->resample_handle->resample_size / mix_buffer->resample_handle->frame_bytes;
|
|
}
|
|
#endif
|
|
AM_LOGE("not support sampleRate %d, only support %d", out_cfg.sampleRate, p_mix_cfg->sampleRate);
|
|
}
|
|
|
|
// Do format convert at here, reduce multi output mixer loading.
|
|
p_format_buf = &mix_buffer->format_buf;
|
|
read_samples = read_frames * out_cfg.channelCnt;
|
|
if (out_cfg.format != p_mix_cfg->format) {
|
|
if (p_format_buf->format != p_mix_cfg->format || p_format_buf->valid_samples != read_samples) {
|
|
size_t req_buf_size = read_samples * audio_bytes_per_sample(p_mix_cfg->format);
|
|
ret = aml_audio_check_and_realloc(&p_format_buf->buf_ptr, &p_format_buf->buf_size, req_buf_size);
|
|
if ((ret != 0) || (p_format_buf->buf_ptr == NULL)) {
|
|
AM_LOGW("allocate format_buf(%zu bytes) failed", req_buf_size);
|
|
} else {
|
|
memcpy_by_audio_format(p_format_buf->buf_ptr, p_mix_cfg->format, out_buffer, out_cfg.format, read_samples);
|
|
out_buffer = p_format_buf->buf_ptr;
|
|
out_cfg.format = p_mix_cfg->format;
|
|
p_format_buf->format = p_mix_cfg->format;
|
|
p_format_buf->valid_samples = read_samples;
|
|
}
|
|
} else {
|
|
out_buffer = p_format_buf->buf_ptr;
|
|
out_cfg.format = p_format_buf->format;
|
|
}
|
|
}
|
|
|
|
if (p_out_buffer) {
|
|
*p_out_buffer = out_buffer;
|
|
}
|
|
if (p_out_cfg) {
|
|
memcpy(p_out_cfg, &out_cfg, sizeof(*p_out_cfg));
|
|
}
|
|
return read_frames;
|
|
}
|
|
|
|
static void _fast_mixer_enable_limiter(aml_pcm_mixing_st *p_sw_mixer, const struct audioCfg *config)
|
|
{
|
|
const float limiter_peak = 1.0f / (1 << AML_FAST_MIXER_HEADROOM);
|
|
const int proc_frames = AML_FAST_MIXER_PROC_FRAMES;
|
|
const int release_time_ms = AML_FAST_MIXER_RELEASE_TIME_MS;
|
|
audio_format_t limiter_format = AUDIO_FORMAT_PCM_32_BIT;
|
|
|
|
if (pcm_mixer_get_limiter_status(p_sw_mixer)) {
|
|
// limiter is enabled, nothing to do.
|
|
return;
|
|
}
|
|
|
|
if (config->format == AUDIO_FORMAT_PCM_FLOAT) {
|
|
limiter_format = AUDIO_FORMAT_PCM_FLOAT;
|
|
}
|
|
pcm_mixer_set_limiter(p_sw_mixer, limiter_peak, proc_frames, release_time_ms, limiter_format);
|
|
pcm_mixer_enable_limiter(p_sw_mixer, true);
|
|
}
|
|
|
|
static inline bool _mixer_inport_to_skip(aml_fast_mixer_inport_st *input_port)
|
|
{
|
|
if (fast_mixer_inport_message_empty(input_port)) {
|
|
if (input_port->state == AML_FAST_MIXER_INPORT_IDLE
|
|
|| input_port->state == AML_FAST_MIXER_INPORT_PAUSED
|
|
|| input_port->state == AML_FAST_MIXER_INPORT_FLUSHED
|
|
|| input_port->state == AML_FAST_MIXER_INPORT_STOPPED
|
|
|| input_port->state == AML_FAST_MIXER_INPORT_STANDBYED) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int aml_fast_mixer_mixing(void *fast_mixer, void *buffer, int bytes, const struct audioCfg *p_mix_config, const aml_fast_mixer_pcm_t pcm_type)
|
|
{
|
|
int index = 0;
|
|
int read_frames = 0;
|
|
int mix_frames = 0;
|
|
void *read_buf = NULL;
|
|
struct audioCfg read_cfg;
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
aml_pcm_mixing_st *p_sw_mixer = NULL;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
if (buffer == NULL || 0 == bytes || p_mix_config == NULL) {
|
|
AM_LOGE("invalid parameter");
|
|
return -EINVAL;
|
|
}
|
|
if (pcm_type < 0 || pcm_type >= AML_FAST_MIXER_PCM_MAX) {
|
|
AM_LOGE("invalid pcm_type %d", pcm_type);
|
|
return -EINVAL;
|
|
}
|
|
p_sw_mixer = mixer->sw_mixer[pcm_type];
|
|
|
|
mix_frames = bytes / p_mix_config->frame_size;
|
|
|
|
if (mixer->limiter_enable) {
|
|
_fast_mixer_enable_limiter(p_sw_mixer, p_mix_config);
|
|
}
|
|
|
|
pthread_mutex_lock(&mixer->lock);
|
|
static_pcm_mixer_start(p_sw_mixer, p_mix_config, buffer, bytes);
|
|
|
|
for (index = 0; index < AML_FAST_MIXER_INPORT_NUM; index++) {
|
|
input_port = mixer->inports[index];
|
|
if (input_port == NULL) {
|
|
continue;
|
|
}
|
|
if (_mixer_inport_to_skip(input_port)) {
|
|
if (get_debug_value(AML_DEBUG_AUDIOHAL_FAST_MIXER)) {
|
|
AM_LOGD("%s port %d, state %d, skip", mixer->name, index, input_port->state);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// reading data ...
|
|
read_frames = _mixer_inport_process(mixer, index, mix_frames, &read_buf, &read_cfg, p_mix_config);
|
|
if (read_frames <= 0) {
|
|
continue;
|
|
}
|
|
|
|
// mixing data when type is SW
|
|
if (!input_port->use_hw_mixer) {
|
|
pcm_mixer_add_data(p_sw_mixer, read_buf, read_frames, &read_cfg);
|
|
}
|
|
}
|
|
mixer->data_used = true;
|
|
|
|
static_pcm_mixer_end(p_sw_mixer);
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _mixer_inport_planar_convert(aml_fast_mixer_inport_st *input_port,
|
|
const void *buffer, int ch_num, int frames, audio_format_t format)
|
|
{
|
|
int i = 0;
|
|
int convert_frames = 0;
|
|
size_t req_buf_size = 0;
|
|
aml_planar_buf_st *p_dst_buf = NULL;
|
|
|
|
if (input_port == NULL || ch_num > AML_PLANAR_MAX_CH_NUM) {
|
|
return 0;
|
|
}
|
|
p_dst_buf = &input_port->data_planar_buf;
|
|
|
|
// data is ready, not ready to convert again
|
|
if (p_dst_buf->data_valid
|
|
&& p_dst_buf->ch_num == ch_num
|
|
&& p_dst_buf->format == format
|
|
&& p_dst_buf->per_ch_frames == frames) {
|
|
return frames;
|
|
}
|
|
|
|
req_buf_size = audio_bytes_per_sample(format) * frames;
|
|
for (i = 0; i < ch_num; i++) {
|
|
if (aml_audio_check_and_realloc(&p_dst_buf->ch_data[i], &p_dst_buf->ch_buf_size[i], req_buf_size) != 0) {
|
|
AM_LOGE("realloc %zu bytes fail !", req_buf_size);
|
|
return 0;
|
|
}
|
|
}
|
|
convert_frames = aml_interleaved_to_planar_pcm(buffer, ch_num, frames, format, p_dst_buf);
|
|
return convert_frames;
|
|
}
|
|
|
|
|
|
int aml_fast_mixer_planar_mix(void *fast_mixer, void **ppdata, int frames, int ch_num,
|
|
int sample_rate, audio_format_t format, bool unlimited_float, const aml_fast_mixer_pcm_t pcm_type)
|
|
{
|
|
int index = 0;
|
|
int read_frames = 0;
|
|
void *read_buf = NULL;
|
|
struct audioCfg read_cfg;
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *p_mixer = fast_mixer;
|
|
aml_pcm_mixing_st *p_sw_mixer = NULL;
|
|
aml_planar_buf_st main_planar_buf;
|
|
aml_planar_buf_st *p_in_planar_buf = NULL;
|
|
struct audioCfg mix_config;
|
|
|
|
if (p_mixer == NULL || !p_mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
if (ppdata == NULL || frames <= 0 || ch_num <= 0 || ch_num > AML_PLANAR_MAX_CH_NUM) {
|
|
AM_LOGE("invalid param, ppdata %p, frames %d, ch_num %d", ppdata, frames, ch_num);
|
|
return -EINVAL;
|
|
}
|
|
p_sw_mixer = p_mixer->sw_mixer[0];
|
|
if (p_sw_mixer == NULL || !p_mixer->data_is_planar) {
|
|
AM_LOGE("error, p_sw_mixer %p, data_is_planar %d", p_sw_mixer, p_mixer->data_is_planar);
|
|
return -1;
|
|
}
|
|
|
|
aml_planar_fill_buf(&main_planar_buf, ppdata, frames, ch_num, format);
|
|
mix_config.sampleRate = sample_rate;
|
|
mix_config.channelCnt = ch_num;
|
|
mix_config.channelMask = audio_channel_out_mask_from_count(ch_num);
|
|
mix_config.format = format;
|
|
mix_config.frame_size = audio_bytes_per_sample(format);
|
|
|
|
pthread_mutex_lock(&p_mixer->lock);
|
|
aml_static_planar_pcm_mixer_start(p_sw_mixer, &main_planar_buf);
|
|
|
|
for (index = 0; index < AML_FAST_MIXER_INPORT_NUM; index++) {
|
|
input_port = p_mixer->inports[index];
|
|
if (input_port == NULL) {
|
|
continue;
|
|
}
|
|
if (_mixer_inport_to_skip(input_port)) {
|
|
if (get_debug_value(AML_DEBUG_AUDIOHAL_FAST_MIXER)) {
|
|
AM_LOGD("%s port %d, state %d, skip", p_mixer->name, index, input_port->state);
|
|
}
|
|
continue;
|
|
}
|
|
if (input_port->param.encoder_exclusive && pcm_type != AML_FAST_MIXER_PCM_ENCODE) {
|
|
if (get_debug_value(AML_DEBUG_AUDIOHAL_FAST_MIXER)) {
|
|
AM_LOGD("%s port %d, encoder_exclusive, pcm_type %d, skip", p_mixer->name, index, pcm_type);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// reading data ...
|
|
read_frames = _mixer_inport_process(p_mixer, index, frames, &read_buf, &read_cfg, &mix_config);
|
|
if (read_frames <= 0) {
|
|
continue;
|
|
}
|
|
|
|
// convert interleaved pcm to input port data_planar_buf
|
|
_mixer_inport_planar_convert(input_port, read_buf, read_cfg.channelCnt, read_frames, read_cfg.format);
|
|
p_in_planar_buf = &input_port->data_planar_buf;
|
|
|
|
// mixing data when type is SW
|
|
if (!input_port->use_hw_mixer) {
|
|
planar_pcm_mixer_add_data(p_sw_mixer, p_in_planar_buf, unlimited_float);
|
|
}
|
|
}
|
|
p_mixer->data_used = true;
|
|
|
|
aml_static_planar_pcm_mixer_end(p_sw_mixer);
|
|
pthread_mutex_unlock(&p_mixer->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int aml_fast_mixer_config(void *fast_mixer, aml_fast_mixer_cfg_type_t cfg_type, void* value)
|
|
{
|
|
int ret = 0;
|
|
int enable = 0;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (!mixer || !value) {
|
|
AM_LOGE("invalid parameter");
|
|
return -EINVAL;
|
|
}
|
|
pthread_mutex_lock(&mixer->lock);
|
|
|
|
switch (cfg_type) {
|
|
case AML_FAST_MIXER_CFG_TYPE_DROP:
|
|
enable = (*(int *)value != 0);
|
|
if (enable != mixer->drop_enable || get_debug_value(AML_DEBUG_AUDIOHAL_FAST_MIXER)) {
|
|
AM_LOGI("%s AML_FAST_MIXER_CFG_TYPE_DROP, value %d", mixer->name, enable);
|
|
}
|
|
mixer->drop_enable = enable;
|
|
break;
|
|
default:
|
|
//add open function for other input type here
|
|
AM_LOGE("%s cfg_type %d not supported", mixer->name, cfg_type);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
return ret;
|
|
}
|
|
|
|
bool aml_fast_mixer_output_is_stable(void *fast_mixer)
|
|
{
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL) {
|
|
AM_LOGE("fast_mixer is NULL");
|
|
return false;
|
|
}
|
|
if (mixer->drop_enable) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int aml_fast_mixer_inports_read(void *fast_mixer, int request_frame)
|
|
{
|
|
int ret = 0;
|
|
int index = 0;
|
|
int read_frame = 0;
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
|
|
pthread_mutex_lock(&mixer->lock);
|
|
|
|
for (index = 0; index < AML_FAST_MIXER_INPORT_NUM; index++) {
|
|
input_port = mixer->inports[index];
|
|
if (input_port == NULL) {
|
|
continue;
|
|
}
|
|
if (_mixer_inport_to_skip(input_port)) {
|
|
if (get_debug_value(AML_DEBUG_AUDIOHAL_FAST_MIXER)) {
|
|
AM_LOGD("%s port %d, state %d, skip", mixer->name, index, input_port->state);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// reading data ...
|
|
read_frame = _mixer_inport_read(mixer, index, request_frame);
|
|
if (read_frame > ret) {
|
|
ret = read_frame;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int aml_fast_mixer_inports_invalid(void *fast_mixer)
|
|
{
|
|
int index = 0;
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return -1;
|
|
}
|
|
|
|
pthread_mutex_lock(&mixer->lock);
|
|
for (index = 0; index < AML_FAST_MIXER_INPORT_NUM; index++) {
|
|
input_port = mixer->inports[index];
|
|
if (input_port == NULL) {
|
|
continue;
|
|
}
|
|
_mixer_inport_invalid(input_port);
|
|
}
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool aml_fast_mixer_inports_active(void *fast_mixer)
|
|
{
|
|
int index = 0;
|
|
bool ret = false;
|
|
aml_fast_mixer_inport_st *input_port = NULL;
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return false;
|
|
}
|
|
|
|
pthread_mutex_lock(&mixer->lock);
|
|
for (index = 0; index < AML_FAST_MIXER_INPORT_NUM; index++) {
|
|
input_port = mixer->inports[index];
|
|
if (input_port == NULL) {
|
|
continue;
|
|
}
|
|
if (!_mixer_inport_to_skip(input_port)) {
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&mixer->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
bool aml_fast_mixer_get_data_used(const void *fast_mixer)
|
|
{
|
|
const aml_fast_mixer_st *mixer = fast_mixer;
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return false;
|
|
}
|
|
return mixer->data_used;
|
|
}
|
|
|
|
void aml_fast_mixer_set_data_used(void *fast_mixer, bool used)
|
|
{
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
if (mixer == NULL || !mixer->initialized) {
|
|
AM_LOGE("mixer is NULL or not initialized");
|
|
return;
|
|
}
|
|
mixer->data_used = used;
|
|
}
|
|
|
|
void aml_fast_mixer_enable_limiter(void *fast_mixer, bool enable)
|
|
{
|
|
aml_fast_mixer_st *mixer = fast_mixer;
|
|
if (mixer == NULL) {
|
|
AM_LOGE("mixer is NULL");
|
|
return;
|
|
}
|
|
mixer->limiter_enable = enable;
|
|
}
|