398 lines
12 KiB
C
398 lines
12 KiB
C
/*
|
|
* 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;
|
|
}
|
|
}
|
|
|