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

Commit c7b2f9dc authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by Android Git Automerger
Browse files

am 402dfba6: Merge "Add support for level measurements in Visualizer" into klp-dev

* commit '402dfba6':
  Add support for level measurements in Visualizer
parents 3f079bb9 402dfba6
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -114,6 +114,14 @@ public:
    status_t setScalingMode(uint32_t mode);
    uint32_t getScalingMode() { return mScalingMode; }

    // set which measurements are done on the audio buffers processed by the effect.
    // valid measurements (mask): MEASUREMENT_MODE_PEAK_RMS
    status_t setMeasurementMode(uint32_t mode);
    uint32_t getMeasurementMode() { return mMeasurementMode; }

    // return a set of int32_t measurements
    status_t getIntMeasurements(uint32_t type, uint32_t number, int32_t *measurements);

    // return a capture in PCM 8 bit unsigned format. The size of the capture is equal to
    // getCaptureSize()
    status_t getWaveForm(uint8_t *waveform);
@@ -156,6 +164,7 @@ private:
    uint32_t mCaptureSize;
    uint32_t mSampleRate;
    uint32_t mScalingMode;
    uint32_t mMeasurementMode;
    capture_cbk_t mCaptureCallBack;
    void *mCaptureCbkUser;
    sp<CaptureThread> mCaptureThread;
+137 −17
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <string.h>
#include <new>
#include <time.h>
#include <math.h>
#include <audio_effects/effect_visualizer.h>


@@ -54,6 +55,18 @@ enum visualizer_state_e {

#define CAPTURE_BUF_SIZE 65536 // "64k should be enough for everyone"

#define DISCARD_MEASUREMENTS_TIME_MS 2000 // discard measurements older than this number of ms

// maximum number of buffers for which we keep track of the measurements
#define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25


struct BufferStats {
    bool mIsValid;
    uint16_t mPeakU16; // the positive peak of the absolute value of the samples in a buffer
    float mRmsSquared; // the average square of the samples in a buffer
};

struct VisualizerContext {
    const struct effect_interface_s *mItfe;
    effect_config_t mConfig;
@@ -65,11 +78,34 @@ struct VisualizerContext {
    uint32_t mLatency;
    struct timespec mBufferUpdateTime;
    uint8_t mCaptureBuf[CAPTURE_BUF_SIZE];
    // for measurements
    uint8_t mChannelCount; // to avoid recomputing it every time a buffer is processed
    uint32_t mMeasurementMode;
    uint8_t mMeasurementWindowSizeInBuffers;
    uint8_t mMeasurementBufferIdx;
    BufferStats mPastMeasurements[MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS];
};

//
//--- Local functions
//
uint32_t Visualizer_getDeltaTimeMsFromUpdatedTime(VisualizerContext* pContext) {
    uint32_t deltaMs = 0;
    if (pContext->mBufferUpdateTime.tv_sec != 0) {
        struct timespec ts;
        if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
            time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec;
            long nsec = ts.tv_nsec - pContext->mBufferUpdateTime.tv_nsec;
            if (nsec < 0) {
                --secs;
                nsec += 1000000000;
            }
            deltaMs = secs * 1000 + nsec / 1000000;
        }
    }
    return deltaMs;
}


void Visualizer_reset(VisualizerContext *pContext)
{
@@ -165,9 +201,21 @@ int Visualizer_init(VisualizerContext *pContext)
    pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
    pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;

    // visualization initialization
    pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
    pContext->mScalingMode = VISUALIZER_SCALING_MODE_NORMALIZED;

    // measurement initialization
    pContext->mChannelCount = popcount(pContext->mConfig.inputCfg.channels);
    pContext->mMeasurementMode = MEASUREMENT_MODE_NONE;
    pContext->mMeasurementWindowSizeInBuffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS;
    pContext->mMeasurementBufferIdx = 0;
    for (uint8_t i=0 ; i<pContext->mMeasurementWindowSizeInBuffers ; i++) {
        pContext->mPastMeasurements[i].mIsValid = false;
        pContext->mPastMeasurements[i].mPeakU16 = 0;
        pContext->mPastMeasurements[i].mRmsSquared = 0;
    }

    Visualizer_setConfig(pContext, &pContext->mConfig);

    return 0;
@@ -270,6 +318,30 @@ int Visualizer_process(
        return -EINVAL;
    }

    // perform measurements if needed
    if (pContext->mMeasurementMode & MEASUREMENT_MODE_PEAK_RMS) {
        // find the peak and RMS squared for the new buffer
        uint32_t inIdx;
        int16_t maxSample = 0;
        float rmsSqAcc = 0;
        for (inIdx = 0 ; inIdx < inBuffer->frameCount * pContext->mChannelCount ; inIdx++) {
            if (inBuffer->s16[inIdx] > maxSample) {
                maxSample = inBuffer->s16[inIdx];
            } else if (-inBuffer->s16[inIdx] > maxSample) {
                maxSample = -inBuffer->s16[inIdx];
            }
            rmsSqAcc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]);
        }
        // store the measurement
        pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mPeakU16 = (uint16_t)maxSample;
        pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mRmsSquared =
                rmsSqAcc / (inBuffer->frameCount * pContext->mChannelCount);
        pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mIsValid = true;
        if (++pContext->mMeasurementBufferIdx >= pContext->mMeasurementWindowSizeInBuffers) {
            pContext->mMeasurementBufferIdx = 0;
        }
    }

    // all code below assumes stereo 16 bit PCM output and input
    int32_t shift;

@@ -423,6 +495,12 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
            p->vsize = sizeof(uint32_t);
            *replySize += sizeof(uint32_t);
            break;
        case VISUALIZER_PARAM_MEASUREMENT_MODE:
            ALOGV("get mMeasurementMode = %d", pContext->mMeasurementMode);
            *((uint32_t *)p->data + 1) = pContext->mMeasurementMode;
            p->vsize = sizeof(uint32_t);
            *replySize += sizeof(uint32_t);
            break;
        default:
            p->status = -EINVAL;
        }
@@ -452,6 +530,10 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
            pContext->mLatency = *((uint32_t *)p->data + 1);
            ALOGV("set mLatency = %d", pContext->mLatency);
            break;
        case VISUALIZER_PARAM_MEASUREMENT_MODE:
            pContext->mMeasurementMode = *((uint32_t *)p->data + 1);
            ALOGV("set mMeasurementMode = %d", pContext->mMeasurementMode);
            break;
        default:
            *(int32_t *)pReplyData = -EINVAL;
        }
@@ -470,24 +552,12 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
        }
        if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
            int32_t latencyMs = pContext->mLatency;
            uint32_t deltaMs = 0;
            if (pContext->mBufferUpdateTime.tv_sec != 0) {
                struct timespec ts;
                if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
                    time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec;
                    long nsec = ts.tv_nsec - pContext->mBufferUpdateTime.tv_nsec;
                    if (nsec < 0) {
                        --secs;
                        nsec += 1000000000;
                    }
                    deltaMs = secs * 1000 + nsec / 1000000;
            const uint32_t deltaMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext);
            latencyMs -= deltaMs;
            if (latencyMs < 0) {
                latencyMs = 0;
            }
                }
            }
            uint32_t deltaSmpl = pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;
            const uint32_t deltaSmpl = pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;

            int32_t capturePoint = pContext->mCaptureIdx - pContext->mCaptureSize - deltaSmpl;
            int32_t captureSize = pContext->mCaptureSize;
@@ -525,6 +595,56 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,

        break;

    case VISUALIZER_CMD_MEASURE: {
        uint16_t peakU16 = 0;
        float sumRmsSquared = 0.0f;
        uint8_t nbValidMeasurements = 0;
        // reset measurements if last measurement was too long ago (which implies stored
        // measurements aren't relevant anymore and shouldn't bias the new one)
        const int32_t delayMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext);
        if (delayMs > DISCARD_MEASUREMENTS_TIME_MS) {
            ALOGE("Discarding measurements, last measurement is %dms old", delayMs);
            for (uint8_t i=0 ; i<pContext->mMeasurementWindowSizeInBuffers ; i++) {
                pContext->mPastMeasurements[i].mIsValid = false;
                pContext->mPastMeasurements[i].mPeakU16 = 0;
                pContext->mPastMeasurements[i].mRmsSquared = 0;
            }
            pContext->mMeasurementBufferIdx = 0;
        } else {
            // only use actual measurements, otherwise the first RMS measure happening before
            // MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
            // low
            for (uint8_t i=0 ; i < pContext->mMeasurementWindowSizeInBuffers ; i++) {
                if (pContext->mPastMeasurements[i].mIsValid) {
                    if (pContext->mPastMeasurements[i].mPeakU16 > peakU16) {
                        peakU16 = pContext->mPastMeasurements[i].mPeakU16;
                    }
                    if (pContext->mMeasurementWindowSizeInBuffers != 0) {
                        sumRmsSquared += pContext->mPastMeasurements[i].mRmsSquared;
                    }
                    nbValidMeasurements++;
                }
            }
        }
        float rms = nbValidMeasurements == 0 ? 0.0f : sqrtf(sumRmsSquared / nbValidMeasurements);
        int32_t* pIntReplyData = (int32_t*)pReplyData;
        // convert from I16 sample values to mB and write results
        if (rms < 0.000016f) {
            pIntReplyData[MEASUREMENT_IDX_RMS] = -9600; //-96dB
        } else {
            pIntReplyData[MEASUREMENT_IDX_RMS] = (int32_t) (2000 * log10(rms / 32767.0f));
        }
        if (peakU16 == 0) {
            pIntReplyData[MEASUREMENT_IDX_PEAK] = -9600; //-96dB
        } else {
            pIntReplyData[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peakU16 / 32767.0f));
        }
        ALOGV("LEVEL_MONITOR_CMD_MEASURE peak=%d (%dmB), rms=%.1f (%dmB)",
                peakU16, pIntReplyData[MEASUREMENT_IDX_PEAK],
                rms, pIntReplyData[MEASUREMENT_IDX_RMS]);
        }
        break;

    default:
        ALOGW("Visualizer_command invalid command %d",cmdCode);
        return -EINVAL;
+68 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ Visualizer::Visualizer (int32_t priority,
        mCaptureSize(CAPTURE_SIZE_DEF),
        mSampleRate(44100000),
        mScalingMode(VISUALIZER_SCALING_MODE_NORMALIZED),
        mMeasurementMode(MEASUREMENT_MODE_NONE),
        mCaptureCallBack(NULL),
        mCaptureCbkUser(NULL)
{
@@ -186,6 +187,73 @@ status_t Visualizer::setScalingMode(uint32_t mode) {
    return status;
}

status_t Visualizer::setMeasurementMode(uint32_t mode) {
    if ((mode != MEASUREMENT_MODE_NONE)
            //Note: needs to be handled as a mask when more measurement modes are added
            && ((mode & MEASUREMENT_MODE_PEAK_RMS) != mode)) {
        return BAD_VALUE;
    }

    Mutex::Autolock _l(mCaptureLock);

    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
    effect_param_t *p = (effect_param_t *)buf32;

    p->psize = sizeof(uint32_t);
    p->vsize = sizeof(uint32_t);
    *(int32_t *)p->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
    *((int32_t *)p->data + 1)= mode;
    status_t status = setParameter(p);

    ALOGV("setMeasurementMode mode %d  status %d p->status %d", mode, status, p->status);

    if (status == NO_ERROR) {
        status = p->status;
        if (status == NO_ERROR) {
            mMeasurementMode = mode;
        }
    }
    return status;
}

status_t Visualizer::getIntMeasurements(uint32_t type, uint32_t number, int32_t *measurements) {
    if (mMeasurementMode == MEASUREMENT_MODE_NONE) {
        ALOGE("Cannot retrieve int measurements, no measurement mode set");
        return INVALID_OPERATION;
    }
    if (!(mMeasurementMode & type)) {
        // measurement type has not been set on this Visualizer
        ALOGE("Cannot retrieve int measurements, requested measurement mode 0x%x not set(0x%x)",
                type, mMeasurementMode);
        return INVALID_OPERATION;
    }
    // only peak+RMS measurement supported
    if ((type != MEASUREMENT_MODE_PEAK_RMS)
            // for peak+RMS measurement, the results are 2 int32_t values
            || (number != 2)) {
        ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d",
                        number);
        return BAD_VALUE;
    }

    status_t status = NO_ERROR;
    if (mEnabled) {
        uint32_t replySize = number * sizeof(int32_t);
        status = command(VISUALIZER_CMD_MEASURE,
                sizeof(uint32_t)  /*cmdSize*/,
                &type /*cmdData*/,
                &replySize, measurements);
        ALOGV("getMeasurements() command returned %d", status);
        if ((status == NO_ERROR) && (replySize == 0)) {
            status = NOT_ENOUGH_DATA;
        }
    } else {
        ALOGV("getMeasurements() disabled");
        return INVALID_OPERATION;
    }
    return status;
}

status_t Visualizer::getWaveForm(uint8_t *waveform)
{
    if (waveform == NULL) {