405 lines
15 KiB
C
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
|