Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b07670f authored by Steve Kondik's avatar Steve Kondik Committed by Gerrit Code Review
Browse files

Merge "Add loudness compensation to eq" into gingerbread

parents c4ae375b 85abcce5
Loading
Loading
Loading
Loading
+83 −21
Original line number Original line Diff line number Diff line
@@ -61,17 +61,16 @@ static int64_t toFixedPoint(float in) {
}
}


EffectEqualizer::EffectEqualizer()
EffectEqualizer::EffectEqualizer()
    : mLoudnessAdjustment(10000.f), mLoudness(0.f)
{
{
    for (int32_t i = 0; i < 5; i ++) {
    for (int32_t i = 0; i < 5; i ++) {
        mBand[i] = 0;
        mBand[i] = 0;
    }
    }
    refreshBands();
}
}


int32_t EffectEqualizer::command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData, uint32_t* replySize, void* pReplyData)
int32_t EffectEqualizer::command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdData, uint32_t* replySize, void* pReplyData)
{
{
	if (cmdCode == EFFECT_CMD_CONFIGURE) {
	if (cmdCode == EFFECT_CMD_CONFIGURE) {
		LOGI("EFFECT_CMD_CONFIGURE");
		int32_t ret = Effect::configure(pCmdData);
		int32_t ret = Effect::configure(pCmdData);
		if (ret != 0) {
		if (ret != 0) {
			LOGE("EFFECT_CMD_CONFIGURE failed");
			LOGE("EFFECT_CMD_CONFIGURE failed");
@@ -80,21 +79,20 @@ int32_t EffectEqualizer::command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdD
			return 0;
			return 0;
		}
		}


		refreshBands();
                /* Weigher for estimating bass compensation. Our adjustments have range from 62.5 to 4000 Hz.
                 * Most of the adjustment is at the 62.5 Hz band, so we must concentrate on bass region. */
                mWeigher.setLowPass(1000.0, mSamplingRate, sqrtf(2)/2.);


		int32_t *replyData = (int32_t *) pReplyData;
		int32_t *replyData = (int32_t *) pReplyData;
		*replyData = 0;
		*replyData = 0;
		LOGI("EFFECT_CMD_CONFIGURE OK");
		return 0;
		return 0;
	}
	}


	if (cmdCode == EFFECT_CMD_GET_PARAM) {
	if (cmdCode == EFFECT_CMD_GET_PARAM) {
		effect_param_t *cep = (effect_param_t *) pCmdData;
		effect_param_t *cep = (effect_param_t *) pCmdData;
		LOGI("EFFECT_CMD_GET_PARAM + %d bytes of param", cep->psize);
		if (cep->psize == 4) {
		if (cep->psize == 4) {
			int cmd = ((int *) cep)[3];
			int32_t cmd = ((int32_t *) cep)[3];
			if (cmd == EQ_PARAM_NUM_BANDS) {
			if (cmd == EQ_PARAM_NUM_BANDS) {
				LOGI("Requested param: EQ_PARAM_NUM_BANDS");
				reply1x4_1x2_t *replyData = (reply1x4_1x2_t *) pReplyData;
				reply1x4_1x2_t *replyData = (reply1x4_1x2_t *) pReplyData;
				replyData->status = 0;
				replyData->status = 0;
				replyData->vsize = 2;
				replyData->vsize = 2;
@@ -104,7 +102,6 @@ int32_t EffectEqualizer::command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdD
				return 0;
				return 0;
			}
			}
			if (cmd == EQ_PARAM_LEVEL_RANGE) {
			if (cmd == EQ_PARAM_LEVEL_RANGE) {
				LOGI("Requested param: EQ_PARAM_LEVEL_RANGE");
				reply1x4_2x2_t *replyData = (reply1x4_2x2_t *) pReplyData;
				reply1x4_2x2_t *replyData = (reply1x4_2x2_t *) pReplyData;
				replyData->status = 0;
				replyData->status = 0;
				replyData->vsize = 4;
				replyData->vsize = 4;
@@ -115,19 +112,16 @@ int32_t EffectEqualizer::command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdD
				return 0;
				return 0;
			}
			}
			if (cmd == EQ_PARAM_GET_NUM_OF_PRESETS) {
			if (cmd == EQ_PARAM_GET_NUM_OF_PRESETS) {
				LOGI("Requested param: EQ_PARAM_GET_NUM_OF_PRESETS");
				reply1x4_1x2_t *replyData = (reply1x4_1x2_t *) pReplyData;
				reply1x4_1x2_t *replyData = (reply1x4_1x2_t *) pReplyData;
				replyData->status = 0;
				replyData->status = 0;
				replyData->vsize = 2;
				replyData->vsize = 2;
				replyData->data = 0;
				replyData->data = 0;
				*replySize = sizeof(reply1x4_1x2_t);
				*replySize = sizeof(reply1x4_1x2_t);
				LOGI("EQ_PARAM_GET_NUM_OF_PRESETS OK");
				return 0;
				return 0;
			}
			}
		} else if (cep->psize == 8) {
		} else if (cep->psize == 8) {
			int cmd = ((int *) cep)[3];
			int32_t cmd = ((int32_t *) cep)[3];
			int arg = ((int *) cep)[4];
			int32_t arg = ((int32_t *) cep)[4];
			LOGI("Requested param: %d, %d", cmd, arg);
			if (cmd == EQ_PARAM_BAND_LEVEL && arg >= 0 && arg < 5) {
			if (cmd == EQ_PARAM_BAND_LEVEL && arg >= 0 && arg < 5) {
				reply2x4_1x2_t *replyData = (reply2x4_1x2_t *) pReplyData;
				reply2x4_1x2_t *replyData = (reply2x4_1x2_t *) pReplyData;
				replyData->status = 0;
				replyData->status = 0;
@@ -158,9 +152,19 @@ int32_t EffectEqualizer::command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdD


	if (cmdCode == EFFECT_CMD_SET_PARAM) {
	if (cmdCode == EFFECT_CMD_SET_PARAM) {
		effect_param_t *cep = (effect_param_t *) pCmdData;
		effect_param_t *cep = (effect_param_t *) pCmdData;
		LOGI("EFFECT_CMD_SET_PARAM, %d", cep->psize);
		int32_t *replyData = (int32_t *) pReplyData;
		int32_t *replyData = (int32_t *) pReplyData;


		if (cep->psize == 6) {
			int32_t cmd = ((int32_t *) cep)[3];
			if (cmd == CUSTOM_EQ_PARAM_LOUDNESS_CORRECTION) {
				int16_t value = ((int16_t *) cep)[8];
				mLoudnessAdjustment = value / 100.0f;
				LOGI("Setting loudness correction reference to %f dB", mLoudnessAdjustment);
				*replyData = 0;
				return 0;
			}
		}

		if (cep->psize == 8) {
		if (cep->psize == 8) {
			int32_t cmd = ((int32_t *) cep)[3];
			int32_t cmd = ((int32_t *) cep)[3];
			int32_t arg = ((int32_t *) cep)[4];
			int32_t arg = ((int32_t *) cep)[4];
@@ -170,7 +174,6 @@ int32_t EffectEqualizer::command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdD
				*replyData = 0;
				*replyData = 0;
				int16_t value = ((int16_t *) cep)[10];
				int16_t value = ((int16_t *) cep)[10];
				mBand[arg] = value / 100.0f;
				mBand[arg] = value / 100.0f;
				refreshBands();
				return 0;
				return 0;
			}
			}
		}
		}
@@ -183,12 +186,48 @@ int32_t EffectEqualizer::command(uint32_t cmdCode, uint32_t cmdSize, void* pCmdD
	return Effect::command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
	return Effect::command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
}
}


/* Source material: ISO 226:2003 curves.
 *
 * On differencing 100 dB curves against 80 dB, 60 dB, 40 dB and 20 dB, a pattern
 * can be established where each loss of 20 dB of power in signal suggests gradually
 * decreasing ear sensitivity, until the bottom is reached at 20 dB SPL where no more
 * boosting is required. Measurements end at 100 dB, which is assumed to be the reference
 * sound pressure level.
 *
 * The boost can be calculated as linear scaling of the following adjustment:
 *   62.5 Hz +24 dB
 *    250 Hz +10 dB
 *   1000 Hz  0 dB
 *   4000 Hz -6 dB
 *
 * The boost will be applied maximally for signals of 20 dB and less,
 * and linearly decreased for signals 20 dB ... 100 dB, and no adjustment is
 * made for 100 dB or higher. User must configure a reference level that maps the
 * digital sound level against the audio.
 */
float EffectEqualizer::getAdjustedBand(int32_t band) {
    const float adj[5] = { 24.0, 10.0, 0.0, -6.0, 0.0 };

    float loudnessLevel = mLoudness + mLoudnessAdjustment;
    if (loudnessLevel > 100.f) {
        loudnessLevel = 100.f;
    }
    if (loudnessLevel < 20.f) {
        loudnessLevel = 20.f;
    }

    loudnessLevel = (loudnessLevel - 20) / (100.0 - 20.0);

    /* Maximum loudness = no adj (reference behavior at 100 dB) */
    return mBand[band] + adj[band] * (1. - loudnessLevel);
}

void EffectEqualizer::refreshBands()
void EffectEqualizer::refreshBands()
{
{
    mGain = toFixedPoint(powf(10.0f, mBand[0] / 20.0f));
    mGain = toFixedPoint(powf(10.0f, getAdjustedBand(0) / 20.0f));
    for (int band = 0; band < 4; band ++) {
    for (int32_t band = 0; band < 4; band ++) {
        float centerFrequency = 62.5f * powf(4, band);
        float centerFrequency = 62.5f * powf(4, band);
        float dB = mBand[band+1] - mBand[band];
        float dB = getAdjustedBand(band+1) - getAdjustedBand(band);


        mFilterL[band].setHighShelf(centerFrequency * 2.0f, mSamplingRate, dB, 1.0f);
        mFilterL[band].setHighShelf(centerFrequency * 2.0f, mSamplingRate, dB, 1.0f);
        mFilterR[band].setHighShelf(centerFrequency * 2.0f, mSamplingRate, dB, 1.0f);
        mFilterR[band].setHighShelf(centerFrequency * 2.0f, mSamplingRate, dB, 1.0f);
@@ -197,10 +236,17 @@ void EffectEqualizer::refreshBands()


int32_t EffectEqualizer::process_effect(audio_buffer_t *in, audio_buffer_t *out)
int32_t EffectEqualizer::process_effect(audio_buffer_t *in, audio_buffer_t *out)
{
{
    refreshBands();

    int64_t maximumPowerSquared = 0;
    for (uint32_t i = 0; i < in->frameCount; i ++) {
    for (uint32_t i = 0; i < in->frameCount; i ++) {
        int32_t tmpL = read(in, i * 2);
        int32_t tmpL = read(in, i * 2);
        int32_t tmpR = read(in, i * 2 + 1);
        int32_t tmpR = read(in, i * 2 + 1);


        /* Calculate signal loudness estimate */
        int64_t weight = mWeigher.process(tmpL + tmpR);
        maximumPowerSquared += weight * weight;
     
        /* first "shelve" is just gain */ 
        /* first "shelve" is just gain */ 
        tmpL = tmpL * mGain >> 32;
        tmpL = tmpL * mGain >> 32;
        tmpR = tmpR * mGain >> 32;
        tmpR = tmpR * mGain >> 32;
@@ -214,6 +260,22 @@ int32_t EffectEqualizer::process_effect(audio_buffer_t *in, audio_buffer_t *out)
        write(out, i * 2, tmpL);
        write(out, i * 2, tmpL);
        write(out, i * 2 + 1, tmpR);
        write(out, i * 2 + 1, tmpR);
    }
    }
    maximumPowerSquared /= in->frameCount;

    float signalPowerDb = logf(maximumPowerSquared / float(int64_t(1) << 48) + 1e-10f) / logf(10.0f) * 10.0f;
    signalPowerDb += 96.0f;

    /* Limit automatic EQ to maximum of 10 dB adjustment rate per second.
     * a frame-to-frame adjusted should not be very large because adjustments do cause
     * small glitches at the output. These may be audible if single step is too large. */
    float maxAdj = in->frameCount / mSamplingRate * 10.f;
    if (mLoudness < signalPowerDb - maxAdj) {
        mLoudness += maxAdj;
    } else if (mLoudness > signalPowerDb + maxAdj) {
        mLoudness -= maxAdj;
    } else {
        mLoudness = signalPowerDb;
    }


    return 0;
    return 0;
}
}
+11 −0
Original line number Original line Diff line number Diff line
@@ -5,13 +5,24 @@
#include "Biquad.h"
#include "Biquad.h"
#include "Effect.h"
#include "Effect.h"


#define CUSTOM_EQ_PARAM_LOUDNESS_CORRECTION 1000

class EffectEqualizer : public Effect {
class EffectEqualizer : public Effect {
    private:
    private:
    /* Equalizer */
    float mBand[5];
    float mBand[5];

    int64_t mGain;
    int64_t mGain;
    Biquad mFilterL[4], mFilterR[4];
    Biquad mFilterL[4], mFilterR[4];


    /* Automatic equalizer */
    float mLoudnessAdjustment;

    Biquad mWeigher;
    float mLoudness;

    void setBand(int32_t idx, float dB);   
    void setBand(int32_t idx, float dB);   
    float getAdjustedBand(int32_t idx);
    void refreshBands();
    void refreshBands();


    public:
    public: