Files
BPI-CM9-Linux/aml-comp/multimedia/aml_audio_hal/utils/aml_volume_utils.c
T

398 lines
12 KiB
C
Raw Normal View History

2026-08-13 11:28:40 +08:00
/*
* hardware/amlogic/audio/utils/aml_volume_utils.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This source code is subject to the terms and conditions defined in the
* file 'LICENSE' which is part of this source code package.
*
*/
#define LOG_TAG "aml_volume_utils"
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include "aml_audio_log.h"
#include "aml_volume_utils.h"
#define FLOAT_ZERO (0.00002) /*Same as defined in audio_hw.h*/
/* default volume cruve in dB. 101 index.
0 is mute, 100 is the max volume.
normally, 0dB is the max volume*/
#define AUDIO_VOLUME_INDEX 101
static float volume_cruve_in_dB[AUDIO_VOLUME_INDEX] = {
VOLUME_MIN_DB, /*mute*/
-60, -53, -47.5, -43.5, -39, -36, -34, -32, -30, -28, /*1-10*/
-27, -26, -25, -24, -23, -22.2, -21.5, -21, -20.6, -20.3, /*11-20*/
-19.9, -19.5, -19, -18.7, -18.4, -18.2, -18, -17.8, -17.5, -17.3, /*21-30*/
-17, -16.8, -16.5, -16.2, -15.9, -15.6, -15.4, -15.2, -14.9, -14.7, /*31-40*/
-14.4, -14.1, -13.9, -13.7, -13.5, -13.3, -13.1, -12.9, -12.7, -12.4, /*41-50*/
-12.1, -11.8, -11.6, -11.4, -11.2, -11, -10.8, -10.6, -10.3, -10, /*51-60*/
-9.8, -9.6, -9.4, -9.2, -9, -8.7, -8.4, -8.1, -7.8, -7.5, /*61-70*/
-7.2, -6.9, -6.7, -6.4, -6.1, -5.8, -5.5, -5.2, -5, -4.8, /*71-80*/
-4.7, -4.5, -4.3, -4.1, -3.8, -3.6, -3.3, -3, -2.7, -2.5, /*81-90*/
-2.2, -2, -1.8, -1.5, -1.3, -1, -0.8, -0.5, -0.3, 0, /*91-100*/
};
static inline int16_t clamp16(int32_t sample)
{
if ((sample >> 15) ^ (sample >> 31)) {
sample = 0x7FFF ^ (sample >> 31);
}
return sample;
}
static inline int32_t clamp32(int64_t sample)
{
if ((sample >> 31) ^ (sample >> 63)) {
sample = 0x7FFFFFFF ^ (sample >> 63);
}
return sample;
}
void apply_volume(float volume, void *buf, int sample_size, int bytes)
{
int16_t *input16 = (int16_t *)buf;
int32_t *input32 = (int32_t *)buf;
unsigned int i = 0;
if (sample_size == 2) {
for (i = 0; i < bytes / sizeof(int16_t); i++) {
int32_t samp = (int32_t)(input16[i]);
input16[i] = clamp16((int32_t)(volume * samp));
}
} else if (sample_size == 4) {
for (i = 0; i < bytes / sizeof(int32_t); i++) {
int64_t samp = (int64_t)(input32[i]);
input32[i] = clamp32((int64_t)(volume * samp));
}
} else {
ALOGE("%s, unsupported audio format: %d!\n", __FUNCTION__, sample_size);
}
return;
}
static const unsigned int s_ulADPanTbl[22] = {
0x7fffffff, 0x73d72312, 0x68c9a15a, 0x5eb68c8c, 0x557d2ba6, 0x4d0324a4, 0x45377009, 0x3e027edf, 0x3757b810, 0x3128e7a0, 0x2b683272,
0x260ceb46, 0x210e863f, 0x1c62e99d, 0x18043a41, 0x13eb2cd9, 0x1012cfb7, 0x0c761164, 0x090fcbf6, 0x05dc8c52, 0x02d87346, 0x00000000};
void get_left_right_volume(unsigned char panByte,float* left_volume,float* right_volume)
{
int pan_index = panByte;
if (pan_index > 127) {
pan_index = pan_index - 256;
}
float base = 0x7fffffff;
if (pan_index > 0) {
if (pan_index > 21) {
*left_volume = s_ulADPanTbl[21] / base;
*right_volume = s_ulADPanTbl[0] / base;
} else {
*left_volume = s_ulADPanTbl[pan_index] / base;
*right_volume = s_ulADPanTbl[0] / base;
}
} else {
if (pan_index < -21) {
*left_volume = s_ulADPanTbl[0] / base;
*right_volume = s_ulADPanTbl[21] / base;
} else {
*left_volume = s_ulADPanTbl[0] / base;
*right_volume = s_ulADPanTbl[-pan_index] / base;
}
}
}
void apply_volume_pan(unsigned char panByte, void *buf, int sample_size, int bytes)
{
int16_t *input16 = (int16_t *)buf;
int32_t *input32 = (int32_t *)buf;
unsigned int i = 0;
float leftvolume;
float rightvolume;
if ((panByte <= 0)|| (panByte > 0xFF))
{
return ;
}
get_left_right_volume(panByte,&leftvolume,&rightvolume);
if (sample_size == 2) {
for (i = 0; i < bytes / sizeof(int16_t); i++) {
int32_t samp = (int32_t)(input16[i]);
if (0 == i%2)
{
input16[i] = clamp16((int32_t)(leftvolume * samp));
}
else
{
input16[i] = clamp16((int32_t)(rightvolume * samp));
}
}
} else if (sample_size == 4) {
for (i = 0; i < bytes / sizeof(int32_t); i++) {
int64_t samp = (int64_t)(input32[i]);
if (0 == i%2)
{
input32[i] = clamp32((int64_t)(leftvolume * samp));
}
else
{
input32[i] = clamp32((int64_t)(rightvolume * samp));
}
}
} else {
ALOGE("%s, unsupported audio format: %d!\n", __FUNCTION__, sample_size);
}
return;
}
void apply_left_right_volume(void *buf, float leftvolume, float rightvolume, int sample_size, int bytes)
{
int16_t *input16 = (int16_t *)buf;
int32_t *input32 = (int32_t *)buf;
unsigned int i = 0;
if (sample_size == 2) {
for (i = 0; i < bytes / sizeof(int16_t); i++) {
int32_t samp = (int32_t)(input16[i]);
if (0 == i%2)
{
input16[i] = clamp16((int32_t)(leftvolume * samp));
}
else
{
input16[i] = clamp16((int32_t)(rightvolume * samp));
}
}
} else if (sample_size == 4) {
for (i = 0; i < bytes / sizeof(int32_t); i++) {
int64_t samp = (int64_t)(input32[i]);
if (0 == i%2)
{
input32[i] = clamp32((int64_t)(leftvolume * samp));
}
else
{
input32[i] = clamp32((int64_t)(rightvolume * samp));
}
}
} else {
ALOGE("%s, unsupported audio format: %d!\n", __FUNCTION__, sample_size);
}
return;
}
void apply_volume_16to32(float volume, int16_t *in_buf, int32_t *out_buf, int bytes)
{
int16_t *input16 = (int16_t *)in_buf;
int32_t *output32 = (int32_t *)out_buf;
unsigned int i = 0;
for (i = 0; i < bytes / sizeof(int16_t); i++) {
int32_t samp = ((int32_t)input16[i]) << 16;
output32[i] = clamp32((int64_t)(samp * (double)(volume)));
}
return;
}
void apply_volume_fade(float last_volume, float volume, void *buf, int sample_size, int channels, int bytes)
{
int16_t *input16 = (int16_t *)buf;
int32_t *input32 = (int32_t *)buf;
int out_frames = 0;
bool needs_fade = false;
float gain_step = 0.0, new_volume = 1.0;
unsigned int i = 0, j = 0;
if (channels == 0 || sample_size == 0 || bytes == 0) {
AM_LOGE("Invalid param: ch:%d sample_size:%d bytes:%d", channels, sample_size, bytes);
return;
}
out_frames = bytes / (channels * sample_size);
if (out_frames == 0) {
AM_LOGE("out_frames is 0");
return;
}
needs_fade = (fabsf(last_volume - volume) >= FLOAT_ZERO);
gain_step = needs_fade ? ((volume - last_volume) / out_frames) : 0.0;
//Avoid redundant calculations when gain_step=0
if (sample_size == 2) {
if (!needs_fade) {
for (i = 0; i < out_frames; ++i) {
for (j = 0; j < channels; ++j) {
input16[i * channels + j] = clamp16((int32_t)(volume * input16[i * channels + j]));
}
}
} else {
for (i = 0; i < out_frames; ++i) {
new_volume = last_volume + i * gain_step;
for (j = 0; j < channels; ++j) {
input16[i * channels + j] = clamp16((int32_t)(new_volume * input16[i * channels + j]));
}
}
}
} else if (sample_size == 4) {
if (!needs_fade) {
for (i = 0; i < out_frames; ++i) {
for (j = 0; j < channels; ++j) {
input32[i * channels + j] = clamp32((int64_t)(volume * input32[i * channels + j]));
}
}
} else {
for (i = 0; i < out_frames; ++i) {
new_volume = last_volume + i * gain_step;
for (j = 0; j < channels; ++j) {
input32[i * channels + j] = clamp32((int64_t)(new_volume * input32[i * channels + j]));
}
}
}
} else {
ALOGE("%s, unsupported audio format: %d!\n", __FUNCTION__, sample_size);
}
return;
}
//This API supports using the same buffer for input and output(in_buf == out_buf)
void apply_volume_fade_16to32(float last_volume, float volume, int16_t *in_buf, int32_t *out_buf, int channels, int bytes)
{
int16_t *input16 = (int16_t*)(in_buf);
int32_t *output32 = (int32_t*)(out_buf);
int32_t out_frames = 0;
int i = 0, j = 0;
float new_volume = 1.0, gain_step = 0.0;
if (channels == 0 || bytes == 0) {
return;
}
out_frames = bytes / (channels * 2);
if (out_frames == 0) {
return;
}
if (fabsf(last_volume - volume) < FLOAT_ZERO) {
for (i = out_frames - 1; i >= 0; i--) {
for (j = channels - 1; j >= 0; j--) {
int32_t samp = (int32_t)(input16[i * channels + j]) << 16;
output32[i * channels + j] = clamp32((int64_t)(samp * (double)(volume)));
}
}
} else {
// If the volume changes, calculate the gradient gain
gain_step = (volume - last_volume) / out_frames;
for (i = out_frames - 1; i >= 0; i--) {
new_volume = last_volume + i * gain_step;
for (j = channels - 1; j >= 0; j--) {
int32_t samp = (int32_t)(input16[i * channels + j]) << 16;
output32[i * channels + j] = clamp32((int64_t)(samp * (double)(new_volume)));
}
}
}
return;
}
static inline float clamp(double sample)
{
if (sample > 1.0)
sample = 1.0;
else if (sample < -1.0)
sample = -1.0;
return sample;
}
void apply_volume_float(float volume, float *buf, int samples)
{
for (int i = 0; i < samples; i++) {
buf[i] = clamp((double)volume * buf[i]);
}
return;
}
float get_volume_by_index(int volume_index)
{
float volume = 1.0;
if (volume_index >= AUDIO_VOLUME_INDEX) {
ALOGE("%s, invalid index!\n", __FUNCTION__);
return volume;
}
if (volume_index >= 0) {
volume *= DbToAmpl(volume_cruve_in_dB[volume_index]);
}
return volume;
}
float get_db_by_index(int volume_index)
{
float db = 0.0;
if (volume_index >= AUDIO_VOLUME_INDEX) {
ALOGE("%s, invalid index!\n", __FUNCTION__);
return VOLUME_MIN_DB;
}
if (volume_index >= 0) {
db = volume_cruve_in_dB[volume_index];
}
return db;
}
// inVol range [0.0---1.0]
int volume2Ms12DBGain(float inVol)
{
float fTargetDB;
// MS12 parameter is INT: -96 is mute
int iMS12DB = -96;
if (inVol > 1 || inVol < 0) {
ALOGE("%s, invalid volume %f\n", __FUNCTION__, inVol);
inVol = 1;
}
// As compared by human ear, AmplToDb() is better than get_db_by_index() here.
fTargetDB = AmplToDb(inVol);
// backup method here
//int iVolIdx;
//fTargetDB = get_db_by_index(iVolIdx);
if (VOLUME_MIN_DB >= fTargetDB) {
fTargetDB = -96;
}
iMS12DB = (int)(fTargetDB - 0.5);
return iMS12DB;
}
static signed int jj=0;
static signed short sinetonedata[108] = {
514,988,1443,1917,2359,2821,3246,3684,4091,4499,4881,5254,5606,5941,6257,6549,
6824,7069,7300,7498,7679,7828,7956,8054,8126,8173,8189,8183,8146,8085,7993,7879,
7735,7569,7375,7159,6916,6653,6367,6059,5733,5385,5021,4639,4245,3833,3411,2976,
2532,2080,1619,1156,686,216,-257,-727,-1196,-1661,-2120,-2571,-3015,-3448,-3871,-4280,
-4675,-5055,-5415,-5763,-6088,-6394,-6677,-6940,-7179,-7394,-7584,-7750,-7890,-8004,-8092,-8151,
-8185,-8191,-8171,-8123,-8048,-7947,-7819,-7664,-7484,-7280,-7051,-6801,-6526,-6231,-5913,-5578,
-5223,-4851,-4463,-4061,-3644,-3215,-2777,-2328,-1873,-1411,-944,-473
};
void appply_tone_16bit2ch(unsigned char* buf, int datalen)
{
int ii =0;
for (ii=0;ii< datalen;) {
signed short *pL = (signed short *)(&buf[ii]);
signed short *pR = pL + 1;
*pL = sinetonedata[jj];
*pR = sinetonedata[jj];
ii += 4;
jj += 1;
jj = jj % 108;
}
}