/* * 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 #include #include #include #include #include #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; } }