Files
BPI-CM9-Linux/aml-comp/multimedia/aml_audio_hal/utils/aml_hw_mixer.c
T
2026-08-24 10:03:24 +08:00

405 lines
15 KiB
C

/*
* Copyright (C) 2017 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_mixer"
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "aml_audio_log.h"
#include "aml_hw_mixer.h"
#include "aml_malloc_debug.h"
#include "aml_audio_log.h"
//code here for audio hw mixer when hwsync with af mixer output stream output
//at the same,need do a software mixer in audio hw c.
int aml_hw_mixer_init(struct aml_hw_mixer *mixer)
{
int ret = 0;
pthread_mutex_init(&mixer->lock, NULL);
pthread_mutex_lock(&mixer->lock);
mixer->wp = 0;
mixer->rp = 0;
if (mixer->buf_size == 0)
mixer->buf_size = AML_HW_MIXER_BUF_SIZE;
mixer->start_buf = aml_audio_calloc(1, mixer->buf_size);
if (!mixer->start_buf) {
AM_LOGE("no mem");
ret = -ENOMEM;
goto exit;
}
mixer->need_cache_flag = 1;
if (mixer->tmp_buf_size == 0) {
mixer->tmp_buf_size = 1024;
}
mixer->tmp_buf = aml_audio_calloc(1, mixer->tmp_buf_size);
if (!mixer->tmp_buf) {
AM_LOGE("no mem, mixer->tmp_buf calloc fail");
ret = -ENOMEM;
aml_audio_free(mixer->start_buf);
mixer->start_buf = NULL;
goto exit;
}
exit:
pthread_mutex_unlock(&mixer->lock);
AM_LOGI("done, mixer %p, mix buf %p, tmp_buf%p", mixer, mixer->start_buf, mixer->tmp_buf);
return ret;
}
void aml_hw_mixer_deinit(struct aml_hw_mixer *mixer)
{
pthread_mutex_lock(&mixer->lock);
AM_LOGI("mixer %p, mix buf %p, tmp_buf %p", mixer, mixer->start_buf, mixer->tmp_buf);
aml_audio_free(mixer->start_buf);
mixer->start_buf = NULL;
aml_audio_free(mixer->tmp_buf);
mixer->tmp_buf = NULL;
mixer->wp = 0;
mixer->rp = 0;
mixer->buf_size = 0;
mixer->tmp_buf_size = 0;
mixer->need_cache_flag = 0;
pthread_mutex_unlock(&mixer->lock);
}
void aml_hw_mixer_reset(struct aml_hw_mixer *mixer)
{
pthread_mutex_lock(&mixer->lock);
mixer->wp = 0;
mixer->rp = 0;
mixer->need_cache_flag = 1;
memset(mixer->start_buf, 0, mixer->buf_size);
pthread_mutex_unlock(&mixer->lock);
AM_LOGI("done, mixer %p, mix buf %p", mixer, mixer->start_buf);
}
static uint32_t aml_hw_mixer_get_space(struct aml_hw_mixer *mixer)
{
unsigned space;
if (mixer->wp >= mixer->rp)
space = mixer->buf_size - (mixer->wp - mixer->rp);
else
space = mixer->rp - mixer->wp;
return space > 64 ? (space - 64) : 0;
}
int aml_hw_mixer_get_content_l(struct aml_hw_mixer *mixer)
{
unsigned content = 0;
if (mixer->wp >= mixer->rp) {
content = mixer->wp - mixer->rp;
} else {
content = mixer->wp - mixer->rp + mixer->buf_size;
}
//ALOGI("wp %d,rp %d\n",mixer->wp,mixer->rp);
//ALOGI("%s(), buffer level = %d", __func__, content);
return content;
}
//we assume the cached size is always smaller than buffer size
//need called by device mutux locked
int aml_hw_mixer_write(struct aml_hw_mixer *mixer, const void *buffer, size_t bytes)
{
int retry = 15;
unsigned tail, space, write_bytes = bytes;
/*coverity[missing_lock]: mixer->start_buf does not need to be lock*/
if (!mixer || !mixer->start_buf) {
AM_LOGE("no mixer or mixer not inited!");
return bytes;
}
while (retry--) {
pthread_mutex_lock(&mixer->lock);
space = aml_hw_mixer_get_space(mixer);
if (space < bytes) {
pthread_mutex_unlock(&mixer->lock);
usleep(10 * 1000);
} else
break;
}
if (retry < 0) {
AM_LOGE("write data no space,space %d,bytes %zu,rp %d,wp %d, reset all ptr",
space, bytes, mixer->rp, mixer->wp);
mixer->wp = 0;
mixer->rp = 0;
return 0;
}
if (write_bytes + mixer->wp > mixer->buf_size) {
tail = mixer->buf_size - mixer->wp;
memcpy(mixer->start_buf + mixer->wp, buffer, tail);
write_bytes -= tail;
memcpy(mixer->start_buf, (unsigned char*)buffer + tail, write_bytes);
mixer->wp = write_bytes;
} else {
memcpy(mixer->start_buf + mixer->wp, buffer, write_bytes);
mixer->wp += write_bytes;
mixer->wp %= mixer->buf_size;
}
pthread_mutex_unlock(&mixer->lock);
return bytes;
}
static inline short CLIPSHORT(int32_t r)
{
if (r > 32767)
r = 32767;
else if (r < -32768)
r = -32768;
return r;
}
static inline int CLIPINT(int64_t r)
{
if (r > 2147483647)
r = 2147483647;
else if (r < -2147483648)
r = -2147483648;
return r;
}
int aml_hw_mixer_mixing(struct aml_hw_mixer *mixer, void *buffer, int bytes, audio_format_t format)
{
int32_t i, tail;
int32_t cached_bytes, read_bytes = bytes;
if (!mixer || !mixer->start_buf) {
AM_LOGE("no mixer or mixer not inited!");
return 0;
}
pthread_mutex_lock(&mixer->lock);
cached_bytes = aml_hw_mixer_get_content_l(mixer);
if (cached_bytes < bytes) {
AM_LOGV("no enough aux data for mixing, cached %d, need %d\n", cached_bytes, bytes);
pthread_mutex_unlock(&mixer->lock);
return 0;
}
if (format == AUDIO_FORMAT_PCM_32_BIT) {
int64_t tmp;
int32_t *tmp_buffer = (int32_t *)buffer;
int32_t *cached_buf = (int32_t *)(mixer->start_buf + mixer->rp);
if (read_bytes + mixer->rp > mixer->buf_size) {
tail = mixer->buf_size - mixer->rp;
for (i = 0; i < tail / 4; i++) {
tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++;
*tmp_buffer++ = CLIPINT(tmp);
}
read_bytes -= tail;
cached_buf = (int32_t *)mixer->start_buf;
for (i = 0; i < read_bytes / 4; i++) {
tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++;
*tmp_buffer++ = CLIPINT(tmp);
}
mixer->rp = read_bytes;
} else {
for (i = 0; i < read_bytes / 4; i++) {
tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++;
*tmp_buffer++ = CLIPINT(tmp);
}
mixer->rp += read_bytes;
mixer->rp %= mixer->buf_size;
}
} else if (format == AUDIO_FORMAT_PCM_16_BIT) {
int32_t tmp;
int16_t *tmp_buffer = (int16_t *)buffer;
int16_t *cached_buf = (int16_t *)(mixer->start_buf + mixer->rp);
if (read_bytes + mixer->rp > mixer->buf_size) {
tail = mixer->buf_size - mixer->rp;
for (i = 0; i < tail / 2; i++) {
tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++;
*tmp_buffer++ = CLIPSHORT(tmp);
}
read_bytes -= tail;
cached_buf = (int16_t *)mixer->start_buf;
for (i = 0; i < read_bytes / 2; i++) {
tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++;
*tmp_buffer++ = CLIPSHORT(tmp);
}
mixer->rp = read_bytes;
} else {
for (i = 0; i < read_bytes / 2; i++) {
tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++;
*tmp_buffer++ = CLIPSHORT(tmp);
}
mixer->rp += read_bytes;
mixer->rp %= mixer->buf_size;
}
} else {
AM_LOGE("format %#x not supporte!", format);
}
pthread_mutex_unlock(&mixer->lock);
return 0;
}
int aml_hw_mixer_mch_in_mixing(struct aml_hw_mixer *mixer, void *buffer, int in_frames, audio_format_t format, int in_ch)
{
int32_t i, tail, j;
int32_t cached_bytes, read_bytes = in_frames * audio_bytes_per_sample(format) * 2;// system sound is 2ch
int need_mixed_ch = 2;
if (in_ch <= 0) {
AM_LOGE("ch(%d) not support!", in_ch);
return -1;
} else if (in_ch == 1) {
need_mixed_ch = 1;
}
pthread_mutex_lock(&mixer->lock);
cached_bytes = aml_hw_mixer_get_content_l(mixer);
if (cached_bytes < read_bytes) {
read_bytes = cached_bytes;
}
if (format == AUDIO_FORMAT_PCM_16_BIT) {
int32_t tmp;
int16_t *tmp_buffer = (int16_t *)buffer;
int16_t *cached_buf = (int16_t *)(mixer->start_buf + mixer->rp);
if (read_bytes + mixer->rp > mixer->buf_size) {
tail = mixer->buf_size - mixer->rp;
for (i = 0; i < tail / 4; i++) {// tail / 4 is frame num
for (j = 0; j < need_mixed_ch; j++) {
tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++;
*tmp_buffer++ = CLIPSHORT(tmp);
}
if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 16 bit data,we only mix cached_buf to the L/R ch of tmp_buffer
tmp_buffer = tmp_buffer + (in_ch -2);
} else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 16 bit data, we only mix L ch data of cached_buf to tmp_buffer
cached_buf = cached_buf + 1;
}
}
read_bytes -= tail;
cached_buf = (int16_t *)mixer->start_buf;
for (i = 0; i < read_bytes / 4; i++) {// read_bytes / 4 is frame num
for (j = 0; j < need_mixed_ch; j++) {
tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++;
*tmp_buffer++ = CLIPSHORT(tmp);
}
if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 16 bit data,we only mix cached_buf to the L/R ch of tmp_buffer
tmp_buffer = tmp_buffer + (in_ch -2);
} else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 16 bit data, we only mix L ch data of cached_buf to tmp_buffer
cached_buf = cached_buf + 1;
}
}
mixer->rp = read_bytes;
} else {
for (i = 0; i < read_bytes / 4; i++) {// read_bytes / 4 is frame num
for (j = 0; j < need_mixed_ch; j++) {
tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++;
*tmp_buffer++ = CLIPSHORT(tmp);
}
if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 16 bit data,we only mix cached_buf to the L/R ch of tmp_buffer
tmp_buffer = tmp_buffer + (in_ch -2);
} else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 16 bit data, we only mix L ch data of cached_buf to tmp_buffer
cached_buf = cached_buf + 1;
}
}
mixer->rp += read_bytes;
mixer->rp %= mixer->buf_size;
}
} else if (format == AUDIO_FORMAT_PCM_32_BIT) {
int64_t tmp;
int32_t *tmp_buffer = (int32_t *)buffer;
int32_t *cached_buf = (int32_t *)(mixer->start_buf + mixer->rp);
if (read_bytes + mixer->rp > mixer->buf_size) {
tail = mixer->buf_size - mixer->rp;
for (i = 0; i < tail / 8; i++) {// tail / 8 is frame num
for (j = 0; j < need_mixed_ch; j++) {
tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++;
*tmp_buffer++ = CLIPINT(tmp);
}
if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 32 bit data,we only mix cached_buf to the L/R ch of tmp_buffer
tmp_buffer = tmp_buffer + (in_ch -2);
} else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 32 bit data, we only mix L ch data of cached_buf to tmp_buffer
cached_buf = cached_buf + 1;
}
}
read_bytes -= tail;
cached_buf = (int32_t *)mixer->start_buf;
for (i = 0; i < read_bytes / 8; i++) {// read_bytes / 8 is frame num
for (j = 0; j < need_mixed_ch; j++) {
tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++;
*tmp_buffer++ = CLIPINT(tmp);
}
if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 32 bit data,we only mix cached_buf to the L/R ch of tmp_buffer
tmp_buffer = tmp_buffer + (in_ch -2);
} else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 32 bit data, we only mix L ch data of cached_buf to tmp_buffer
cached_buf = cached_buf + 1;
}
}
mixer->rp = read_bytes;
} else {
for (i = 0; i < read_bytes / 8; i++) {// read_bytes / 8 is frame num
for (j = 0; j < need_mixed_ch; j++) {
tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++;
*tmp_buffer++ = CLIPINT(tmp);
}
if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 32 bit data,we only mix cached_buf to the L/R ch of tmp_buffer
tmp_buffer = tmp_buffer + (in_ch -2);
} else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 32 bit data, we only mix L ch data of cached_buf to tmp_buffer
cached_buf = cached_buf + 1;
}
}
mixer->rp += read_bytes;
mixer->rp %= mixer->buf_size;
}
} else {
AM_LOGE("format %#x not support!", format);
}
pthread_mutex_unlock(&mixer->lock);
return 0;
}
//need called by device mutux locked
int aml_hw_mixer_read(struct aml_hw_mixer *mixer, void *r_buf, uint32_t size)
{
unsigned cached_size;
unsigned read_size = size;
unsigned tail = 0;
pthread_mutex_lock(&mixer->lock);
cached_size = aml_hw_mixer_get_content_l(mixer);
// we always assue we have enough data to read when hwsync enabled.
// if we do not have,insert zero data.
if (cached_size < size) {
AM_LOGV("read data has not enough data to mixer,read %d, have %d,rp %d,wp %d\n", size, cached_size, mixer->rp, mixer->wp);
memset((unsigned char*)r_buf + cached_size, 0, size - cached_size);
read_size = cached_size;
}
if (read_size + mixer->rp > mixer->buf_size) {
tail = mixer->buf_size - mixer->rp;
memcpy(r_buf, mixer->start_buf + mixer->rp, tail);
read_size -= tail;
memcpy((unsigned char*)r_buf + tail, mixer->start_buf, read_size);
mixer->rp = read_size;
} else {
memcpy(r_buf, mixer->start_buf + mixer->rp, read_size);
mixer->rp += read_size;
mixer->rp %= mixer->buf_size;
}
pthread_mutex_unlock(&mixer->lock);
return size;
}
// aml audio hw c mixer code end