zoukankan      html  css  js  c++  java
  • EQ实现

    原理参考:

    https://www.cnblogs.com/fellow1988/p/9189338.html

    https://www.cnblogs.com/fellow1988/p/9136346.html

    实现代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<pthread.h>
    #include<math.h>
    typedef struct{
    char chunkId[4];//"RIFF"
    unsigned long chunkSize;
    char format[4];//"WAVE"
    }WAVE_RIFF;
    typedef struct{
    char chunkId[4];//"fmt"
    unsigned long chunkSize;
    unsigned short audioFormat;
    unsigned short chNum;
    unsigned long sampleRate;
    unsigned long byteRate;//SampleRate * NumChannels * BitsPerSample/8
    unsigned short blockAlign;//NumChannels * BitsPerSample/8
    unsigned short bitsPerSample;//8,16,32
    }WAVE_FMT;
    typedef struct{
    char chunkId[4];//"data"
    unsigned long chunkSize;//NumSamples * NumChannels * BitsPerSample/8
    }WAVE_DATA;
    typedef struct
    {
        char fileName[256];
        FILE *fp;
        long pos;
        unsigned long totalSampleNum;
        WAVE_RIFF riffChunk;
        WAVE_FMT fmtChunk;
        WAVE_DATA dataChunk;
    }WAVE_INFO;
    
    #define READ_SAMPLES 1024
    #define PP_SAMPLES 64
    typedef struct
    {
        unsigned short chNum;
        unsigned short bankNum;
        unsigned long samplesPerBank;
        unsigned short bytesPerSample;
        unsigned short bankRp;
        unsigned short bankWp;
        unsigned char ***pData;
        unsigned char fgEos;
        unsigned char fgInited;
    }PP_BUF_T;
    
    typedef enum
    {
        FADER_TYPE_LINE,
        FADER_TYPE_CUBIC,
    }FADER_TYPE_E;
    typedef struct
    {
        float attuationDb;
        FADER_TYPE_E type;
        unsigned long timeMs;
    }FADER_PARAM_T;
    
    typedef struct
    {
        FADER_PARAM_T faderParams;
        unsigned long timeInSample;
        float curVolumDb;
        float curGain;
        float startGain;
        float targetGain;
        unsigned long curSample;
        unsigned long sampleRate;
        float *segGain;
        unsigned short segNum;
    }FADER_HANDLE_T;
    typedef struct
    {
        short **pData;
        unsigned short chNum;
        unsigned short samples;
        unsigned short bytesPerSample;
    }DATA_INFO_T;
    PP_BUF_T gPpBuf;
    FADER_HANDLE_T gFaderHandle;
    unsigned char fgEnd = 0;
    
    typedef struct
    {
        unsigned long attackTimeMs;
        unsigned long releaseTimeMs;
        unsigned short ratio;
        float thresholdDb;
    }DRC_COMPRESSOR_PARAM_T;
    typedef struct
    {
        unsigned long attackTimeMs;
        unsigned long releaseTimeMs;
        float thresholdDb;
    }DRC_LIMITER_PARAM_T;
    typedef struct
    {
        unsigned long attackTimeMs;
        unsigned long releaseTimeMs;
        unsigned long holdTimeMs;
        unsigned short ratio;
        float thresholdDb;
    }DRC_EXPANDER_PARAM_T;
    typedef enum
    {
        DRC_TYPE_COMPRESSOR,
        DRC_TYPE_LIMITER,
        DRC_TYPE_EXPANDER,
        DRC_TYPE_AUTO,
    }DRC_TYPE_E;
    typedef struct
    {
        DRC_TYPE_E eDrcType;
        union {
            DRC_COMPRESSOR_PARAM_T compressorParams;
            DRC_LIMITER_PARAM_T limiterParams;
            DRC_EXPANDER_PARAM_T expanderParams;
        }uDrcParams;
        float curGain;
        float curSmoothGainDb;
        float alphaAttack;
        float alphaRelease;
        unsigned long attackHoldCounter;
        unsigned long releaseHoldCounter;
    }DRC_HANDLE_T;
    
    typedef struct
    {
        float a[3];
        float b[3];
    }FILTER_COEFF_T;
    
    typedef enum
    {
        FILTER_TYPE_LPF,
        FILTER_TYPE_HPF,
        FILTER_TYPE_LSF,
        FILTER_TYPE_HSF,
        FILTER_TYPE_PEF,
        FILTER_TYPE_MAX,
    }FILTER_TYPE_E;
    
    typedef struct
    {
        unsigned long fs;
        unsigned long f0;
        float Q;
        float gainDb;
    }FILTER_PARAM_T;
    
    #define FILTER_MAX_CH 2
    typedef struct
    {
        unsigned short xHistory[FILTER_MAX_CH][3];
        unsigned short yHistory[FILTER_MAX_CH][3];
    }FILTER_HISTORY_T;
    
    typedef struct
    {
        FILTER_TYPE_E eFilterType;
        FILTER_PARAM_T filterParams;
        FILTER_COEFF_T filterCoeff;
        FILTER_HISTORY_T filterHistory;
    }FILTER_HANDLE_T;
    FILTER_HANDLE_T gFilterHandle;
    
    typedef enum
    {
        EQ_MODE_ROCK,
        EQ_MODE_POP,
        EQ_MODE_MAX
    }EQ_MODE_E;
    
    #define EQ_MAX_BAND 6
    typedef struct
    {
        EQ_MODE_E eEqMode;
        FILTER_HANDLE_T filterHandles[EQ_MAX_BAND];
    }EQ_HANDLE_T;
    
    FILTER_TYPE_E gEqFilterTypes[EQ_MAX_BAND] =
    {
        FILTER_TYPE_LSF,
        FILTER_TYPE_PEF,
        FILTER_TYPE_PEF,
        FILTER_TYPE_PEF,
        FILTER_TYPE_PEF,
        FILTER_TYPE_HSF
    };
    
    FILTER_PARAM_T gEqFilterParams[EQ_MODE_MAX][EQ_MAX_BAND] =
    {
        {
            {48000, 100, 2, 9},
            {48000, 600, 8, 3},
            {48000, 1000, 8, -1},
            {48000, 3000, 8, 3},
            {48000, 5000, 8, 6},
            {48000, 10000, 2, 9},
        },
        {
            {48000, 100, 2, -1},
            {48000, 600, 8, 6},
            {48000, 1000, 8, 9},
            {48000, 3000, 8, 6},
            {48000, 3000, 8, 3},
            {48000, 10000, 2, -2},
        }
    };
    
    EQ_HANDLE_T gEqHandle;
    typedef struct
    {
        short sampleValue;
        short bytesPerSample;
    }SAMPLE_INFO_T;
    
    void filterInit(FILTER_HANDLE_T *pFilterHandle, FILTER_TYPE_E eFilterType, FILTER_PARAM_T *pFilterParams)
    {
        float A = pow(10, pFilterParams->gainDb / 40);
        float w0 = 2 * 3.1415926 * pFilterParams->f0 / pFilterParams->fs;
        float cos_w0 = cos(w0);
        float sin_w0 = sin(w0);
        float alpha = sin_w0 / (2 * pFilterParams->Q);
        float *a = pFilterHandle->filterCoeff.a;
        float *b = pFilterHandle->filterCoeff.b;
        pFilterHandle->eFilterType = eFilterType;
        memcpy(&(pFilterHandle->filterParams), pFilterParams, sizeof(FILTER_PARAM_T));
        memset(&(pFilterHandle->filterHistory), 0, sizeof(FILTER_HISTORY_T));
        switch(eFilterType)
        {
            case FILTER_TYPE_LPF:
                b[0] = (1 - cos_w0) / 2;
                b[1] = 1 - cos_w0;
                b[2] = (1 - cos_w0) / 2;
                a[0] = 1 + alpha;
                a[1] =  -2 * cos_w0;
                a[2] = 1- alpha;
                break;
            case FILTER_TYPE_HPF:
                b[0] = (1 + cos_w0) / 2;
                b[1] = -(1 + cos_w0);
                b[2] = (1 + cos_w0) / 2;
                a[0] = 1 + alpha;
                a[1] =  -2 * cos_w0;
                a[2] = 1- alpha;
                break;
            case FILTER_TYPE_LSF:
                b[0] = A * ( (A + 1) - (A - 1) * cos_w0 + 2 * sqrt(A) * alpha);
                b[1] = 2 * A * ((A - 1) - (A + 1) * cos_w0);
                b[2] = A * ((A + 1) - (A - 1) * cos_w0 - 2 * sqrt(A) * alpha);
                a[0] = (A + 1) + (A - 1) * cos_w0 + 2 * sqrt(A) * alpha;
                a[1] = -2 * ((A - 1) + (A + 1) * cos_w0);
                a[2] = (A + 1) + (A - 1) * cos_w0 - 2 * sqrt(A) * alpha;
                break;
            case FILTER_TYPE_HSF:
                b[0] = A * ( (A + 1) + (A - 1) * cos_w0 + 2 * sqrt(A) * alpha);
                b[1] = -2 * A * ((A - 1) + (A + 1) * cos_w0);
                b[2] = A * ((A + 1) + (A - 1) * cos_w0 - 2 * sqrt(A) * alpha);
                a[0] = (A + 1) - (A - 1) * cos_w0 + 2 * sqrt(A) * alpha;
                a[1] = 2 * ((A - 1) - (A + 1) * cos_w0);
                a[2] = (A + 1) - (A - 1) * cos_w0 - 2 * sqrt(A) * alpha;
                break;
            case FILTER_TYPE_PEF:
                b[0] = 1 + alpha * A;
                b[1] = -2 * cos_w0;
                b[2] = 1 - alpha * A;
                a[0] = 1 + alpha / A;
                a[1] = -2 * cos_w0;
                a[2] = 1 - alpha / A;
                break;
            default:
                break;
        }
        b[0] /= a[0];
        b[1] /= a[0];
        b[2] /= a[0];
        a[1] /= a[0];
        a[2] /= a[0];
        a[0] = 1;
        
    }
    
    short filterCore(FILTER_HANDLE_T *pFilterHandle, short curSampleValue, short curChIdx)
    {
        short *x, *y;
        float *a, *b;
        long sum = 0;
        x = pFilterHandle->filterHistory.xHistory[curChIdx];
        y = pFilterHandle->filterHistory.yHistory[curChIdx];
        a = pFilterHandle->filterCoeff.a;
        b = pFilterHandle->filterCoeff.b;
        x[0] = curSampleValue;
        //y[0] = (b[0] * x[0] + b[1] * x[1] + b[2] * x[2] - a[1] * y[1] - a[2] * y[2]) / a[0];
        sum += b[0] * (long)x[0];
        sum += b[1] * (long)x[1];
        sum += b[2] * (long)x[2];
        sum += -a[1] * (long)y[1];
        sum += -a[2] * (long)y[2];
        y[0] = (short)sum;
        x[2] = x[1];
        x[1] = x[0];
        y[2] = y[1];
        y[1] = y[0];
        return y[0];
    }
    
    short levelDown(short sampleValue)
    {
        return sampleValue >> 2;
    }
    
    #define MAX(a, b) (a > b ? a : b)
    #define MIN(a, b) (a < b ? a : b)
    
    short overflowAdd(short a, short b)
    {
        long sum = 0;
        sum = a + b;
        sum = MAX(sum, -32727);
        sum = MIN(sum, 32727);
        return (short)sum;
    }
    
    short levelUp(short sampleValue)
    {
        short sum = 0;
        sum = overflowAdd(sampleValue, sampleValue);
        sum = overflowAdd(sum, sum);
        return sum;
    }
    void filter(FILTER_HANDLE_T *pFilterHandle, DATA_INFO_T *pDataInfo)
    {
        unsigned short sampleIdx, chIdx;
        for (chIdx = 0; chIdx < pDataInfo->chNum; chIdx++)
        {
            for (sampleIdx = 0; sampleIdx < pDataInfo->samples; sampleIdx++)
            {
                pDataInfo->pData[chIdx][sampleIdx] = levelDown(pDataInfo->pData[chIdx][sampleIdx]);
                pDataInfo->pData[chIdx][sampleIdx] = filterCore(pFilterHandle, pDataInfo->pData[chIdx][sampleIdx], chIdx);
                pDataInfo->pData[chIdx][sampleIdx] = levelUp(pDataInfo->pData[chIdx][sampleIdx]);
            }
        }
    }
    
    void eqFilterInit(EQ_HANDLE_T *pEqHandle, EQ_MODE_E eEqMode)
    {
        if (pEqHandle == NULL || eEqMode >= EQ_MODE_MAX || eEqMode < 0)
            return;
        FILTER_HANDLE_T *pFilterHandle;
        FILTER_PARAM_T *pFilterParam;
        short bandIdx;
        for (bandIdx = 0; bandIdx < EQ_MAX_BAND; bandIdx++)
        {
            pFilterHandle = &pEqHandle->filterHandles[bandIdx];
            pFilterParam = &gEqFilterParams[eEqMode][bandIdx];
            filterInit(pFilterHandle, gEqFilterTypes[eEqMode], pFilterParam);
        }
    }
    
    void eqInit(EQ_HANDLE_T *pEqHandle, EQ_MODE_E eEqMode)
    {
        if (pEqHandle == NULL || eEqMode >= EQ_MODE_MAX || eEqMode < 0)
            return;
        pEqHandle->eEqMode = eEqMode;
        eqFilterInit(pEqHandle, eEqMode);
    }
    
    void eq(EQ_HANDLE_T *pEqHandle, DATA_INFO_T *pDataInfo)
    {
        unsigned short sampleIdx, chIdx, bandIdx;
        if (pEqHandle == NULL || pEqHandle->eEqMode >= EQ_MODE_MAX || pEqHandle->eEqMode < 0)
            return ;
        FILTER_HANDLE_T *pFilterHandle;
        for (bandIdx = 0; bandIdx < EQ_MAX_BAND; bandIdx++)
        {
            pFilterHandle = &(pEqHandle->filterHandles[bandIdx]);
            for (chIdx = 0; chIdx < pDataInfo->chNum; chIdx++)
            {
                for (sampleIdx = 0; sampleIdx < pDataInfo->samples; sampleIdx++)
                {
                    pDataInfo->pData[chIdx][sampleIdx] = levelDown(pDataInfo->pData[chIdx][sampleIdx]);
                    pDataInfo->pData[chIdx][sampleIdx] = filterCore(pFilterHandle, pDataInfo->pData[chIdx][sampleIdx], chIdx);
                    pDataInfo->pData[chIdx][sampleIdx] = levelUp(pDataInfo->pData[chIdx][sampleIdx]);
                }
            }
        }
    }
    float dbToGain(float db);
    DRC_HANDLE_T gDrcHandle;
    void drcInit(DRC_HANDLE_T *pDrcHandle, void * pDrcParams, DRC_TYPE_E eDrcType)
    {
        DRC_COMPRESSOR_PARAM_T *pCompressorParams;
        DRC_LIMITER_PARAM_T *pLimiterParams;
        DRC_EXPANDER_PARAM_T *pExpanderParams;
        if (pDrcHandle == NULL || pDrcParams == NULL || eDrcType > DRC_TYPE_AUTO)
            return;
        pDrcHandle->eDrcType = eDrcType;
        switch (eDrcType)
        {
            case DRC_TYPE_COMPRESSOR:
                pCompressorParams = (DRC_COMPRESSOR_PARAM_T *)pDrcParams;
                memcpy(&pDrcHandle->uDrcParams.compressorParams, pCompressorParams, sizeof(DRC_COMPRESSOR_PARAM_T));
                pDrcHandle->alphaAttack = expf(-logf(9) / (48000 * pCompressorParams->attackTimeMs / 1000));
                pDrcHandle->alphaRelease = expf(-logf(9) / (48000 * pCompressorParams->releaseTimeMs / 1000));
                break;
            case DRC_TYPE_LIMITER:
                pLimiterParams = (DRC_LIMITER_PARAM_T *)pDrcParams;
                memcpy(&pDrcHandle->uDrcParams.limiterParams, pLimiterParams, sizeof(DRC_LIMITER_PARAM_T));
                pDrcHandle->alphaAttack = expf(-logf(9) / (48000 * pLimiterParams->attackTimeMs / 1000));
                pDrcHandle->alphaRelease = expf(-logf(9) / (48000 * pLimiterParams->releaseTimeMs / 1000));
                break;
            case DRC_TYPE_EXPANDER:
                pExpanderParams = (DRC_EXPANDER_PARAM_T *)pDrcParams;
                memcpy(&pDrcHandle->uDrcParams.expanderParams, pExpanderParams, sizeof(DRC_EXPANDER_PARAM_T));
                pDrcHandle->alphaAttack = expf(-logf(9) / (48000 * pExpanderParams->attackTimeMs / 1000));
                pDrcHandle->alphaRelease = expf(-logf(9) / (48000 * pExpanderParams->releaseTimeMs /1000));
                break;
            case DRC_TYPE_AUTO:
                break;
        }
        pDrcHandle->curGain = 1;
        pDrcHandle->curSmoothGainDb = 0;
        pDrcHandle->attackHoldCounter = 0;
        pDrcHandle->releaseHoldCounter = 0;
    }
    
    float sampleValueToDb(SAMPLE_INFO_T *pSampleInfo)
    {
        if (pSampleInfo == NULL)
            return 0;
        if (pSampleInfo->sampleValue == 0)
            pSampleInfo->sampleValue = 1;
        short maxSampleValue = ((1 << (pSampleInfo->bytesPerSample * 8)) - 1) / 2;
        float db = 20 * log10f((float)abs(pSampleInfo->sampleValue) / maxSampleValue);
        //printf("maxSampleValue:%d, sampleValue:%d, db:%f
    ", maxSampleValue, pSampleInfo->sampleValue, db);
        return db;
    }
    
    float drcComputeGainDb(DRC_HANDLE_T *pDrcHandle, float sampleDb)
    {
        if (pDrcHandle == NULL)
            return 0;
        float staticChract;
        switch (pDrcHandle->eDrcType)
        {
            case DRC_TYPE_COMPRESSOR:
                if (sampleDb < pDrcHandle->uDrcParams.compressorParams.thresholdDb)
                {
                    staticChract = sampleDb;
                }
                else
                {
                    staticChract = pDrcHandle->uDrcParams.compressorParams.thresholdDb + (sampleDb - pDrcHandle->uDrcParams.compressorParams.thresholdDb) / pDrcHandle->uDrcParams.compressorParams.ratio;
                }
                break;
            case DRC_TYPE_LIMITER:
                if (sampleDb < pDrcHandle->uDrcParams.limiterParams.thresholdDb)
                {
                    staticChract = sampleDb;
                }
                else
                {
                    staticChract = pDrcHandle->uDrcParams.limiterParams.thresholdDb;
                }
                break;
            case DRC_TYPE_EXPANDER:
                if (sampleDb >= pDrcHandle->uDrcParams.expanderParams.thresholdDb)
                {
                    staticChract = sampleDb;
                }
                else
                {
                    staticChract = pDrcHandle->uDrcParams.expanderParams.thresholdDb + (sampleDb - pDrcHandle->uDrcParams.expanderParams.thresholdDb) / pDrcHandle->uDrcParams.expanderParams.ratio;
                }
                break;
            case DRC_TYPE_AUTO:
                break;
        }
        //printf("staticChract:%f, sampleDb:%f
    ", staticChract, sampleDb); 
        return staticChract - sampleDb;
        
    }
    
    float drcCompressorSmoothGain(DRC_HANDLE_T *pDrcHandle, float computeGainDb)
    {
        float smoothGainDb;
        if (computeGainDb < pDrcHandle->curSmoothGainDb)
        {
            smoothGainDb = pDrcHandle->alphaAttack * pDrcHandle->curSmoothGainDb + (1 - pDrcHandle->alphaAttack) * computeGainDb;
        }
        else
        {
            smoothGainDb = pDrcHandle->alphaRelease * pDrcHandle->curSmoothGainDb + (1 - pDrcHandle->alphaRelease) * computeGainDb;
        }
        return smoothGainDb;
    }
    
    float drcExpanderSmoothGain(DRC_HANDLE_T *pDrcHandle, float computeGainDb)
    {
        float smoothGainDb;
        unsigned long holdTimeInSample = pDrcHandle->uDrcParams.expanderParams.holdTimeMs * 48000 / 1000;
        if (pDrcHandle->attackHoldCounter >= holdTimeInSample && computeGainDb > pDrcHandle->curSmoothGainDb)
        {
            smoothGainDb = pDrcHandle->alphaAttack * pDrcHandle->curSmoothGainDb + (1 - pDrcHandle->alphaAttack) * computeGainDb;
        }
        else if (pDrcHandle->attackHoldCounter < holdTimeInSample && computeGainDb > pDrcHandle->curSmoothGainDb)
        {
            smoothGainDb = pDrcHandle->curSmoothGainDb;
            pDrcHandle->attackHoldCounter++;
            pDrcHandle->releaseHoldCounter = 0;
        }
        else if (pDrcHandle->releaseHoldCounter >= holdTimeInSample && computeGainDb <= pDrcHandle->curSmoothGainDb)
        {
            smoothGainDb = pDrcHandle->alphaRelease * pDrcHandle->curSmoothGainDb + (1 - pDrcHandle->alphaRelease) * computeGainDb;
        }
        else if (pDrcHandle->releaseHoldCounter < holdTimeInSample && computeGainDb <= pDrcHandle->curSmoothGainDb)
        {
            smoothGainDb = pDrcHandle->curSmoothGainDb;
            pDrcHandle->releaseHoldCounter++;
            pDrcHandle->attackHoldCounter = 0;
        }
        return smoothGainDb;
    }
    float drcSmoothGain(DRC_HANDLE_T *pDrcHandle, float computeGainDb)
    {
        if (pDrcHandle == NULL)
            return 0;
        float smoothGainDb;
        switch (pDrcHandle->eDrcType)
        {
            case DRC_TYPE_COMPRESSOR:
            case DRC_TYPE_LIMITER:
                smoothGainDb = drcCompressorSmoothGain(pDrcHandle, computeGainDb);
                break;
            case DRC_TYPE_EXPANDER:
                smoothGainDb = drcExpanderSmoothGain(pDrcHandle, computeGainDb);
                break;
            case DRC_TYPE_AUTO:
                break;
        }
        return smoothGainDb;
    }
    void drcCalGain(DRC_HANDLE_T *pDrcHandle, SAMPLE_INFO_T *pSampleInfo)
    {
        if (pDrcHandle == NULL || pSampleInfo == NULL)
            return;
        float sampleDb = sampleValueToDb(pSampleInfo);
        float computeGainDb = drcComputeGainDb(pDrcHandle, sampleDb);
        pDrcHandle->curSmoothGainDb = drcSmoothGain(pDrcHandle, computeGainDb);
        pDrcHandle->curGain = dbToGain(pDrcHandle->curSmoothGainDb);
        printf("sampleDb:%f, computeGainDb:%f, smoothGainDb:%f, curGain:%f
    ",
            sampleDb, computeGainDb, pDrcHandle->curSmoothGainDb, pDrcHandle->curGain);
    }
    
    void drc(DRC_HANDLE_T *pDrcHandle, DATA_INFO_T *pDataInfo)
    {
        unsigned short sampleIdx, chIdx;
        SAMPLE_INFO_T sampleInfo;
        for (chIdx = 0; chIdx < pDataInfo->chNum; chIdx++)
        {
            for (sampleIdx = 0; sampleIdx < pDataInfo->samples; sampleIdx++)
            {
                sampleInfo.bytesPerSample = 2;
                sampleInfo.sampleValue = pDataInfo->pData[chIdx][sampleIdx];
                drcCalGain(pDrcHandle, &sampleInfo);
                pDataInfo->pData[chIdx][sampleIdx] *= pDrcHandle->curGain;
            }
        }
    }
    
    
    float mapSegGainToRealGain(FADER_HANDLE_T *pFaderHandle, float segGain)
    {
        float deltaGain = pFaderHandle->targetGain - pFaderHandle->startGain;
        float realGain = deltaGain * segGain + pFaderHandle->startGain;
        return realGain;
    }
    void faderPrepareShape(FADER_HANDLE_T *pFaderHandle, unsigned short segNum)
    {
        unsigned short segIdx;
        pFaderHandle->segGain = (float *)malloc((segNum + 1) * sizeof(float));
        pFaderHandle->segNum = segNum;
        float tmp;
        if (pFaderHandle->faderParams.type != FADER_TYPE_CUBIC)
             return;
        //0~1 divide into N seg.
        for (segIdx = 0; segIdx < segNum + 1; segIdx++)
        {
            tmp = (float)segIdx / segNum;
            pFaderHandle->segGain[segIdx] = tmp * tmp * tmp;
            pFaderHandle->segGain[segIdx] = mapSegGainToRealGain(pFaderHandle, pFaderHandle->segGain[segIdx]);
        }
    }
    float dbToGain(float db)
    {
        return pow(10, db/20);
    }
    void faderInit(FADER_HANDLE_T *pFaderHandle, float attuationDb, FADER_TYPE_E type, unsigned long timeMs, unsigned long sampleRate, float curVolumDb)
    {
        pFaderHandle->faderParams.attuationDb = attuationDb;
        pFaderHandle->faderParams.type = type;
        pFaderHandle->faderParams.timeMs = timeMs;
        pFaderHandle->timeInSample = timeMs * sampleRate / 1000;
        pFaderHandle->curGain = pFaderHandle->startGain = dbToGain(curVolumDb);
        pFaderHandle->targetGain = dbToGain(curVolumDb + attuationDb);
        pFaderHandle->curSample = 0;
        faderPrepareShape(pFaderHandle, 20);
        printf("faderInit
    ");
    }
    
    void faderCalGain(FADER_HANDLE_T *pFaderHandle)
    {
        float startGainInCurSeg, endGainInCurSeg, step;
        float deltaGain = pFaderHandle->targetGain - pFaderHandle->startGain;
        unsigned long samplesInSeg = pFaderHandle->timeInSample / pFaderHandle->segNum;
        unsigned short curSeg = (float)pFaderHandle->curSample / samplesInSeg;
        unsigned long startSampleInCurSeg = samplesInSeg * curSeg;
        switch (pFaderHandle->faderParams.type)
        {
            case FADER_TYPE_LINE:
                step = deltaGain / pFaderHandle->timeInSample;
                pFaderHandle->curGain += deltaGain / pFaderHandle->timeInSample;
                //pFaderHandle->curGain = pFaderHandle->startGain + deltaGain * pFaderHandle->curSample / pFaderHandle->timeInSample;
                break;
            case FADER_TYPE_CUBIC:
                startGainInCurSeg = pFaderHandle->segGain[curSeg];
                endGainInCurSeg = pFaderHandle->segGain[curSeg + 1];
                step = (endGainInCurSeg - startGainInCurSeg) / samplesInSeg;
                if (pFaderHandle->curSample == startSampleInCurSeg)
                    pFaderHandle->curGain = startGainInCurSeg;
                else
                    pFaderHandle->curGain += step;
                break;
        }
        printf("curGain:%f, curSample:%ld, timeInSample:%ld, curSeg:%d, startGain:%f, endGain:%f
    ", pFaderHandle->curGain, pFaderHandle->curSample, pFaderHandle->timeInSample, curSeg, startGainInCurSeg, endGainInCurSeg);
    }
    
    void fader(FADER_HANDLE_T *pFaderHandle, DATA_INFO_T *pDataInfo)
    {
        unsigned short sampleIdx, chIdx;
        for (sampleIdx = 0; sampleIdx < pDataInfo->samples; sampleIdx++)
        {
            if (pFaderHandle->curSample != pFaderHandle->timeInSample)
            {
                faderCalGain(pFaderHandle);
                pFaderHandle->curSample++;
            }
            for (chIdx = 0; chIdx < pDataInfo->chNum; chIdx++)
            {
                pDataInfo->pData[chIdx][sampleIdx] *= pFaderHandle->curGain;
            }
        }
    }
    void printWaveHeader(WAVE_INFO *pWaveInfo)
    {
        printf("fileName:%s
    ", pWaveInfo->fileName);
        printf("riff chunk:
    ");
        printf("chunkId:%c%c%c%c
    ", pWaveInfo->riffChunk.chunkId[0], pWaveInfo->riffChunk.chunkId[1], pWaveInfo->riffChunk.chunkId[2], pWaveInfo->riffChunk.chunkId[3]);
        printf("chunkSize:%ld
    ", pWaveInfo->riffChunk.chunkSize);
        printf("format:%c%c%c%c
    ", pWaveInfo->riffChunk.format[0], pWaveInfo->riffChunk.format[1], pWaveInfo->riffChunk.format[2], pWaveInfo->riffChunk.format[3]);
        printf("fmt chunk:
    ");
        printf("chunkId:%c%c%c
    ", pWaveInfo->fmtChunk.chunkId[0], pWaveInfo->fmtChunk.chunkId[1], pWaveInfo->fmtChunk.chunkId[2]);
        printf("chunkSize:%ld
    ", pWaveInfo->fmtChunk.chunkSize);
        printf("audioFormat:%d
    ", pWaveInfo->fmtChunk.audioFormat);
        printf("chNum:%d
    ", pWaveInfo->fmtChunk.chNum);
        printf("sampleRate:%ld
    ", pWaveInfo->fmtChunk.sampleRate);
        printf("byteRate:%ld
    ", pWaveInfo->fmtChunk.byteRate);
        printf("blockAlign:%d
    ", pWaveInfo->fmtChunk.blockAlign);
        printf("bitsPerSample:%d
    ", pWaveInfo->fmtChunk.bitsPerSample);
        printf("data chunk:
    ");
        printf("chunkId:%c%c%c%c
    ", pWaveInfo->dataChunk.chunkId[0], pWaveInfo->dataChunk.chunkId[1], pWaveInfo->dataChunk.chunkId[2], pWaveInfo->dataChunk.chunkId[3]);
        printf("chunkSize:%ld
    ", pWaveInfo->dataChunk.chunkSize);
        
    }
    void initWaveInfo(WAVE_INFO *pWaveInfo, unsigned short chNum, unsigned long sampleRate, unsigned short bitsPerSample)
    {
        //strncpy(pWaveInfo->riffChunk.chunkId, "RIFF", 4);
        pWaveInfo->riffChunk.chunkId[0] = 'R';
        pWaveInfo->riffChunk.chunkId[1] = 'I';
        pWaveInfo->riffChunk.chunkId[2] = 'F';
        pWaveInfo->riffChunk.chunkId[3] = 'F';
        pWaveInfo->riffChunk.chunkSize = 0;
        //strncpy(pWaveInfo->riffChunk.format, "WAVE", 4);
        pWaveInfo->riffChunk.format[0] = 'W';
        pWaveInfo->riffChunk.format[1] = 'A';
        pWaveInfo->riffChunk.format[2] = 'V';
        pWaveInfo->riffChunk.format[3] = 'E';
        //strncpy(pWaveInfo->fmtChunk.chunkId, "fmt", 3);
        pWaveInfo->fmtChunk.chunkId[0] = 'f';
        pWaveInfo->fmtChunk.chunkId[1] = 'm';
        pWaveInfo->fmtChunk.chunkId[2] = 't';
        pWaveInfo->fmtChunk.chunkId[3] = ' ';
        pWaveInfo->fmtChunk.chunkSize = sizeof(WAVE_FMT) - 8;
        pWaveInfo->fmtChunk.audioFormat = 1;
        pWaveInfo->fmtChunk.chNum = chNum;
        pWaveInfo->fmtChunk.sampleRate = sampleRate;
        pWaveInfo->fmtChunk.byteRate = sampleRate * chNum * bitsPerSample / 8;
        pWaveInfo->fmtChunk.blockAlign = chNum * bitsPerSample / 8;
        pWaveInfo->fmtChunk.bitsPerSample = bitsPerSample;
        //strncpy(pWaveInfo->dataChunk.chunkId, "data", 4);
        pWaveInfo->dataChunk.chunkId[0] = 'd';
        pWaveInfo->dataChunk.chunkId[1] = 'a';
        pWaveInfo->dataChunk.chunkId[2] = 't';
        pWaveInfo->dataChunk.chunkId[3] = 'a';
        
        pWaveInfo->dataChunk.chunkSize = 0;
        pWaveInfo->totalSampleNum = 0;
        ///printWaveHeader(pWaveInfo);
    }
    
    void rwRiffChunk(WAVE_INFO *pWaveInfo, unsigned char fgRead)
    {
        if (fgRead)
        {
            fread((char *)&pWaveInfo->riffChunk.chunkId, 4, 1, pWaveInfo->fp);
            fread((char *)&pWaveInfo->riffChunk.chunkSize, 4, 1, pWaveInfo->fp);
            fread((char *)&pWaveInfo->riffChunk.format, 4, 1, pWaveInfo->fp);
        }
        else
        {
            fwrite((char *)&pWaveInfo->riffChunk.chunkId, 4, 1, pWaveInfo->fp);
            fwrite((char *)&pWaveInfo->riffChunk.chunkSize, 4, 1, pWaveInfo->fp);
            fwrite((char *)&pWaveInfo->riffChunk.format, 4, 1, pWaveInfo->fp);
        }
    }
    void rwFmtChunk(WAVE_INFO *pWaveInfo, unsigned char fgRead)
    {
        if (fgRead)
        {
            fread((char *)&pWaveInfo->fmtChunk.chunkId, 4, 1, pWaveInfo->fp);
            fread((char *)&pWaveInfo->fmtChunk.chunkSize, 4, 1, pWaveInfo->fp);
            fread((char *)&pWaveInfo->fmtChunk.audioFormat, 2, 1, pWaveInfo->fp);
            fread((char *)&pWaveInfo->fmtChunk.chNum, 2, 1, pWaveInfo->fp);
            fread((char *)&pWaveInfo->fmtChunk.sampleRate, 4, 1, pWaveInfo->fp);
            fread((char *)&pWaveInfo->fmtChunk.byteRate, 4, 1, pWaveInfo->fp);
            fread((char *)&pWaveInfo->fmtChunk.blockAlign, 2, 1, pWaveInfo->fp);
            fread((char *)&pWaveInfo->fmtChunk.bitsPerSample, 2, 1, pWaveInfo->fp);
        }
        else
        {
            fwrite((char *)&pWaveInfo->fmtChunk.chunkId, 4, 1, pWaveInfo->fp);
            fwrite((char *)&pWaveInfo->fmtChunk.chunkSize, 4, 1, pWaveInfo->fp);
            fwrite((char *)&pWaveInfo->fmtChunk.audioFormat, 2, 1, pWaveInfo->fp);
            fwrite((char *)&pWaveInfo->fmtChunk.chNum, 2, 1, pWaveInfo->fp);
            fwrite((char *)&pWaveInfo->fmtChunk.sampleRate, 4, 1, pWaveInfo->fp);
            fwrite((char *)&pWaveInfo->fmtChunk.byteRate, 4, 1, pWaveInfo->fp);
            fwrite((char *)&pWaveInfo->fmtChunk.blockAlign, 2, 1, pWaveInfo->fp);
            fwrite((char *)&pWaveInfo->fmtChunk.bitsPerSample, 2, 1, pWaveInfo->fp);
            
        }
    }
    void rwDataChunk(WAVE_INFO *pWaveInfo, unsigned char fgRead)
    {
        if (fgRead)
        {
            fread((char *)&pWaveInfo->dataChunk.chunkId, 4, 1, pWaveInfo->fp);
            fread((char *)&pWaveInfo->dataChunk.chunkSize, 4, 1, pWaveInfo->fp);
        }
        else
        {
            fwrite((char *)&pWaveInfo->dataChunk.chunkId, 4, 1, pWaveInfo->fp);
            fwrite((char *)&pWaveInfo->dataChunk.chunkSize, 4, 1, pWaveInfo->fp);
        }
    }
    
    void readWaveHeader(char *fileName, WAVE_INFO *pWaveInfo)
    {
        size_t retSize;
        strncpy(pWaveInfo->fileName, fileName, strlen(fileName));
        pWaveInfo->fp = fopen(fileName, "rb");
        if (pWaveInfo->fp == NULL)
        {
            printf("fopen fail, errno:%d
    ", errno);
            return;
        }
        #if 0
        retSize = fread((char *)&pWaveInfo->riffChunk, sizeof(WAVE_RIFF), 1, pWaveInfo->fp);
        retSize = fread((char *)&pWaveInfo->fmtChunk, sizeof(WAVE_FMT), 1, pWaveInfo->fp);
        retSize = fread((char *)&pWaveInfo->dataChunk, sizeof(WAVE_DATA), 1, pWaveInfo->fp);
        #endif
        rwRiffChunk(pWaveInfo, 1);
        rwFmtChunk(pWaveInfo, 1);
        rwDataChunk(pWaveInfo, 1);
        pWaveInfo->pos = ftell(pWaveInfo->fp);
        pWaveInfo->totalSampleNum = pWaveInfo->dataChunk.chunkSize / (pWaveInfo->fmtChunk.bitsPerSample / 8);
        fclose(pWaveInfo->fp);
        printWaveHeader(pWaveInfo);
    }
    
    void initPpBuf(unsigned short chNum, unsigned short bankNum, unsigned long samplesPerBank, unsigned short bytesPerSample)
    {
        unsigned short chIdx, bankIdx;
        gPpBuf.chNum = chNum;    
        gPpBuf.bankNum = bankNum;    
        gPpBuf.samplesPerBank = samplesPerBank;
        gPpBuf.bytesPerSample = bytesPerSample;
    
        gPpBuf.bankRp = gPpBuf.bankWp = 0;
        gPpBuf.fgEos = 0;
        gPpBuf.pData = (unsigned char ***)malloc(chNum * sizeof(unsigned char **));
        for (chIdx = 0; chIdx < chNum; chIdx++)
        {
            gPpBuf.pData[chIdx] = (unsigned char **)malloc(bankNum * sizeof(unsigned char *));
            for (bankIdx =0; bankIdx < bankNum; bankIdx++)
            {
                gPpBuf.pData[chIdx][bankIdx] = (unsigned char *) malloc(samplesPerBank * bytesPerSample * sizeof(unsigned char));
            }
        }
        gPpBuf.fgInited = 1;
    }
    
    int sendData(unsigned char *writeBuffer, unsigned short chNum)
    {
        unsigned short sampleIdx, chIdx, byteIdx;
        //printf("sendData, wp:%d, rp:%d
    ", gPpBuf.bankWp, gPpBuf.bankRp);
        if ((gPpBuf.bankWp + 1 ) % gPpBuf.bankNum == gPpBuf.bankRp)
        {
            //full
            return 1;
        }
        else
        {
            for (sampleIdx = 0; sampleIdx < PP_SAMPLES; sampleIdx++)
            {
                for (chIdx =0; chIdx < chNum; chIdx++)
                {
                    for (byteIdx = 0; byteIdx < gPpBuf.bytesPerSample; byteIdx++)
                    {
                   gPpBuf.pData[chIdx][gPpBuf.bankWp][sampleIdx * gPpBuf.bytesPerSample + byteIdx] = writeBuffer[(chIdx + sampleIdx * chNum) * gPpBuf.bytesPerSample + byteIdx];  
              }
                }
            }
            gPpBuf.bankWp = (gPpBuf.bankWp + 1) % gPpBuf.bankNum;
        }
        return 0;
    }
    
    int recvData(unsigned char **readBuffer)
    {
        unsigned short chIdx;
        //printf("recvData, wp:%d, rp:%d
    ", gPpBuf.bankWp, gPpBuf.bankRp);
        if (gPpBuf.bankWp == gPpBuf.bankRp)
        {
            //empty
            return 1;
        }
        else
        {
            for (chIdx = 0; chIdx < gPpBuf.chNum; chIdx++)
            {
                memcpy(&readBuffer[chIdx][0], &gPpBuf.pData[chIdx][gPpBuf.bankRp][0], PP_SAMPLES * gPpBuf.bytesPerSample * sizeof(unsigned char));
            }
            gPpBuf.bankRp = (gPpBuf.bankRp + 1) % gPpBuf.bankNum;
        }
        return 0;
    }
    void *readThread(void *arg)
    {
        char *fileName = (char *)arg;
        size_t retSize;
        WAVE_INFO waveInfo;
        memset(&waveInfo, 0, sizeof(WAVE_INFO));
        unsigned long bytesPerLoop;
        unsigned short loopIdx, loop;
        unsigned long readCount = 0;
        readWaveHeader(fileName, &waveInfo);
        initPpBuf(waveInfo.fmtChunk.chNum, 3, PP_SAMPLES, 2);
    
        unsigned long readSize = READ_SAMPLES * waveInfo.fmtChunk.chNum * waveInfo.fmtChunk.bitsPerSample / 8;
        printf("readSize:%ld
    ", readSize);
        unsigned char *readBuffer = (unsigned char *)malloc(readSize * sizeof(unsigned char));
        waveInfo.fp = fopen(fileName, "rb");
        fseek(waveInfo.fp,  waveInfo.pos, SEEK_SET);
        while (1)
        {
            retSize = fread(readBuffer, readSize, 1, waveInfo.fp);
            if (retSize <= 0)
            {
                 printf("fread fail,retSize:%d, %s, eof:%d, readCount:%ld
    ", (int) retSize, strerror(errno), feof(waveInfo.fp), readCount);
                 gPpBuf.fgEos = 1;
                 break;
            }
            else
            {
                 bytesPerLoop = PP_SAMPLES *waveInfo.fmtChunk.chNum * waveInfo.fmtChunk.bitsPerSample / 8;
                 loop = readSize / bytesPerLoop;
                 loopIdx = 0;
                 while (loopIdx < loop)
                 {
                     if (0 != sendData(readBuffer + loopIdx * bytesPerLoop, waveInfo.fmtChunk.chNum))
                     {
                         usleep(1000);
                     }
                     else
                     {
                         loopIdx++;
                     }
                 }
                 readCount++; 
            }
        }
        return NULL;
    }
    void pp(DATA_INFO_T *pDataInfo)
    {
        //fader(&gFaderHandle, pDataInfo);
        //drc(&gDrcHandle, pDataInfo);
        //filter(&gFilterHandle, pDataInfo);
        eq(&gEqHandle, pDataInfo);
    }
    
    void saveOneChInWave(unsigned char *pData, unsigned long size, WAVE_INFO *pWaveInfo)
    {
       size_t retSize = 0;
       if (pWaveInfo->fp == NULL)
       {
           pWaveInfo->fp = fopen(pWaveInfo->fileName, "wb");
           #if 0
           retSize = fwrite((char *)&pWaveInfo->riffChunk, sizeof(WAVE_RIFF), 1, pWaveInfo->fp);
           retSize = fwrite((char *)&pWaveInfo->fmtChunk, sizeof(WAVE_FMT), 1, pWaveInfo->fp);
            retSize = fwrite((char *)&pWaveInfo->dataChunk, sizeof(WAVE_DATA), 1, pWaveInfo->fp);
            #endif
            rwRiffChunk(pWaveInfo, 0);
            rwFmtChunk(pWaveInfo, 0);
            rwDataChunk(pWaveInfo, 0);   
        } 
        retSize = fwrite(pData, size, 1, pWaveInfo->fp);
        pWaveInfo->totalSampleNum += (size / pWaveInfo->fmtChunk.chNum / (pWaveInfo->fmtChunk.bitsPerSample / 8));
        pWaveInfo->pos = ftell(pWaveInfo->fp);
    }
    
    void updateWaveHeader(WAVE_INFO *pWaveInfo)
    {
        size_t retSize;
        pWaveInfo->riffChunk.chunkSize = pWaveInfo->pos - 8;
        pWaveInfo->dataChunk.chunkSize = pWaveInfo->totalSampleNum * pWaveInfo->fmtChunk.chNum * pWaveInfo->fmtChunk.bitsPerSample / 8;
        fseek(pWaveInfo->fp,  0, SEEK_SET);
        #if 0
        retSize = fwrite((char *)&pWaveInfo->riffChunk, sizeof(WAVE_RIFF), 1, pWaveInfo->fp);
        retSize = fwrite((char *)&pWaveInfo->fmtChunk, sizeof(WAVE_FMT), 1, pWaveInfo->fp);
        retSize = fwrite((char *)&pWaveInfo->dataChunk, sizeof(WAVE_DATA), 1, pWaveInfo->fp);
        #endif    
        rwRiffChunk(pWaveInfo, 0);
        rwFmtChunk(pWaveInfo, 0);
        rwDataChunk(pWaveInfo, 0);   
        fclose(pWaveInfo->fp);
        
        printWaveHeader(pWaveInfo);
    }
    void *ppThread(void *arg)
    {
        char *fileName = (char *)arg;
        WAVE_INFO waveInfo;
        memset(&waveInfo, 0, sizeof(waveInfo));
        strncpy(waveInfo.fileName, fileName, strlen(fileName));
        printf("out file:%s
    ", waveInfo.fileName);
        waveInfo.fp = NULL;
        while(!gPpBuf.fgInited)
        {
            usleep(1000);
        }
        initWaveInfo(&waveInfo, 1, 48000, 16);
        unsigned char **readBuffer = (unsigned char **)malloc(gPpBuf.chNum * sizeof(unsigned char *));
        unsigned short chIdx;
        for(chIdx = 0; chIdx < gPpBuf.chNum; chIdx++)
        {
            readBuffer[chIdx] = (unsigned char *)malloc(PP_SAMPLES * gPpBuf.bytesPerSample * sizeof(unsigned char));
        }
        while (1)
        {
            if (0 != recvData(readBuffer))
            {
                if (gPpBuf.fgEos)
                    break;
                usleep(1000);
            }
            else
            {
                DATA_INFO_T dataInfo;
                dataInfo.chNum = gPpBuf.chNum;
                dataInfo.samples = PP_SAMPLES;
                dataInfo.bytesPerSample = gPpBuf.bytesPerSample;
                dataInfo.pData = (short **)readBuffer;
                pp(&dataInfo);
                saveOneChInWave(readBuffer[0], PP_SAMPLES * gPpBuf.bytesPerSample, &waveInfo);
            }
        }
        updateWaveHeader(&waveInfo);
        fgEnd = 1;
    }
    
    int main(int argc, char **argv)
    {
    #if 0
        WAVE_INFO inputWaveInfo, outputWaveInfo;
        readWaveHeader(argv[1], &inputWaveInfo);
        //initWaveInfo(&outputWaveInfo, 2, 48000, 16);
    #endif
        
    #if 1
        pthread_t readThreadId, ppThreadId;
        memset(&gPpBuf, 0, sizeof(PP_BUF_T));
       // initPpBuf(6, 3, PP_SAMPLES, 2);
        #if 0
        memset(&gFaderHandle, 0, sizeof(FADER_HANDLE_T));
        float curVolumDb = 0;
        float attuationDb = -5;
        FADER_TYPE_E type = FADER_TYPE_CUBIC;
        unsigned long timeMs = 5000;
        unsigned long sampleRate = 48000;
        faderInit(&gFaderHandle, attuationDb, type, timeMs, sampleRate, curVolumDb);
        #endif
        memset(&gDrcHandle, 0, sizeof(DRC_HANDLE_T));
    #if 0
        DRC_COMPRESSOR_PARAM_T compressorParams;
        compressorParams.thresholdDb = -15;
        compressorParams.attackTimeMs = 1;
        compressorParams.releaseTimeMs = 10;
        compressorParams.ratio = 4;
        drcInit(&gDrcHandle, &compressorParams, DRC_TYPE_COMPRESSOR);    
    #endif
    #if 0
        DRC_LIMITER_PARAM_T limiterParams;
        limiterParams.thresholdDb = -15;
        limiterParams.attackTimeMs = 20;
        limiterParams.releaseTimeMs = 200;
        drcInit(&gDrcHandle, &limiterParams, DRC_TYPE_LIMITER);
    #endif
    #if 0 
        DRC_EXPANDER_PARAM_T expanderParams;
        expanderParams.thresholdDb = -30;
        expanderParams.attackTimeMs = 10;
        expanderParams.releaseTimeMs = 100;
        expanderParams.ratio = 4;
        expanderParams.holdTimeMs = 0;
        drcInit(&gDrcHandle, &expanderParams, DRC_TYPE_EXPANDER);
    #endif
    #if 0
        FILTER_PARAM_T filterParams;
        memset(&filterParams, 0, sizeof(FILTER_PARAM_T));
        filterParams.fs = 48000;
        filterParams.f0 = 500;
        filterParams.Q = 0.707;
        filterInit(&gFilterHandle, FILTER_TYPE_LPF, &filterParams);
    #endif
    #if 0
        FILTER_PARAM_T filterParams;
        memset(&filterParams, 0, sizeof(FILTER_PARAM_T));
        filterParams.fs = 48000;
        filterParams.f0 = 4000;
        filterParams.Q = 0.707;
        filterParams.gainDb = 6;
        filterInit(&gFilterHandle, FILTER_TYPE_LSF, &filterParams);
    #endif
    #if 0
        FILTER_PARAM_T filterParams;
        memset(&filterParams, 0, sizeof(FILTER_PARAM_T));
        filterParams.fs = 48000;
        filterParams.f0 = 8000;
        filterParams.Q = 0.707;
        filterInit(&gFilterHandle, FILTER_TYPE_HPF, &filterParams);
    #endif
    #if 0
        FILTER_PARAM_T filterParams;
        memset(&filterParams, 0, sizeof(FILTER_PARAM_T));
        filterParams.fs = 48000;
        filterParams.f0 = 8000;
        filterParams.Q = 0.707;
        filterParams.gainDb = 6;
        filterInit(&gFilterHandle, FILTER_TYPE_HSF, &filterParams);
    #endif
        eqInit(&gEqHandle, EQ_MODE_POP);
        pthread_create(&readThreadId, NULL, readThread, argv[1]);
        pthread_create(&ppThreadId, NULL, ppThread, argv[2]);
        while(!fgEnd)
        {
            sleep(1);
        }
    #endif
        return 0;
    }
  • 相关阅读:
    destoon自定义函数获取地区名称
    dt开发之-自定义函数获取分类名称
    dt二次开发之-地区链接伪静态标签用法
    DT二次开发之-采购页面加入好看的倒计时
    DT二次开发之-资讯列表中调用 TAG 关键词
    DT系统中tag如何使用like与%来进行模糊查询
    如何防护DDOS攻击策略
    织梦阿里云OSS解决方案
    漫画解读“跨视图粒度计算”,了解有数分析利器
    [译] 关于 SPA,你需要掌握的 4 层 (1)
  • 原文地址:https://www.cnblogs.com/fellow1988/p/9747448.html
Copyright © 2011-2022 走看看