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[];//"RIFF"
unsigned long chunkSize;
char format[];//"WAVE"
}WAVE_RIFF;
typedef struct{
char chunkId[];//"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[];//"data"
unsigned long chunkSize;//NumSamples * NumChannels * BitsPerSample/8
}WAVE_DATA;
typedef struct
{
char fileName[];
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 = ; 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[];
float b[];
}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][];
unsigned short yHistory[FILTER_MAX_CH][];
}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] =
{
{
{, , , },
{, , , },
{, , , -},
{, , , },
{, , , },
{, , , },
},
{
{, , , -},
{, , , },
{, , , },
{, , , },
{, , , },
{, , , -},
}
}; 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(, pFilterParams->gainDb / );
float w0 = * 3.1415926 * pFilterParams->f0 / pFilterParams->fs;
float cos_w0 = cos(w0);
float sin_w0 = sin(w0);
float alpha = sin_w0 / ( * 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), , sizeof(FILTER_HISTORY_T));
switch(eFilterType)
{
case FILTER_TYPE_LPF:
b[] = ( - cos_w0) / ;
b[] = - cos_w0;
b[] = ( - cos_w0) / ;
a[] = + alpha;
a[] = - * cos_w0;
a[] = - alpha;
break;
case FILTER_TYPE_HPF:
b[] = ( + cos_w0) / ;
b[] = -( + cos_w0);
b[] = ( + cos_w0) / ;
a[] = + alpha;
a[] = - * cos_w0;
a[] = - alpha;
break;
case FILTER_TYPE_LSF:
b[] = A * ( (A + ) - (A - ) * cos_w0 + * sqrt(A) * alpha);
b[] = * A * ((A - ) - (A + ) * cos_w0);
b[] = A * ((A + ) - (A - ) * cos_w0 - * sqrt(A) * alpha);
a[] = (A + ) + (A - ) * cos_w0 + * sqrt(A) * alpha;
a[] = - * ((A - ) + (A + ) * cos_w0);
a[] = (A + ) + (A - ) * cos_w0 - * sqrt(A) * alpha;
break;
case FILTER_TYPE_HSF:
b[] = A * ( (A + ) + (A - ) * cos_w0 + * sqrt(A) * alpha);
b[] = - * A * ((A - ) + (A + ) * cos_w0);
b[] = A * ((A + ) + (A - ) * cos_w0 - * sqrt(A) * alpha);
a[] = (A + ) - (A - ) * cos_w0 + * sqrt(A) * alpha;
a[] = * ((A - ) - (A + ) * cos_w0);
a[] = (A + ) - (A - ) * cos_w0 - * sqrt(A) * alpha;
break;
case FILTER_TYPE_PEF:
b[] = + alpha * A;
b[] = - * cos_w0;
b[] = - alpha * A;
a[] = + alpha / A;
a[] = - * cos_w0;
a[] = - alpha / A;
break;
default:
break;
}
b[] /= a[];
b[] /= a[];
b[] /= a[];
a[] /= a[];
a[] /= a[];
a[] = ; } short filterCore(FILTER_HANDLE_T *pFilterHandle, short curSampleValue, short curChIdx)
{
short *x, *y;
float *a, *b;
long sum = ;
x = pFilterHandle->filterHistory.xHistory[curChIdx];
y = pFilterHandle->filterHistory.yHistory[curChIdx];
a = pFilterHandle->filterCoeff.a;
b = pFilterHandle->filterCoeff.b;
x[] = 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[] * (long)x[];
sum += b[] * (long)x[];
sum += b[] * (long)x[];
sum += -a[] * (long)y[];
sum += -a[] * (long)y[];
y[] = (short)sum;
x[] = x[];
x[] = x[];
y[] = y[];
y[] = y[];
return y[];
} short levelDown(short sampleValue)
{
return sampleValue >> ;
} #define MAX(a, b) (a > b ? a : b)
#define MIN(a, b) (a < b ? a : b) short overflowAdd(short a, short b)
{
long sum = ;
sum = a + b;
sum = MAX(sum, -);
sum = MIN(sum, );
return (short)sum;
} short levelUp(short sampleValue)
{
short sum = ;
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 = ; chIdx < pDataInfo->chNum; chIdx++)
{
for (sampleIdx = ; 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 < )
return;
FILTER_HANDLE_T *pFilterHandle;
FILTER_PARAM_T *pFilterParam;
short bandIdx;
for (bandIdx = ; 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 < )
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 < )
return ;
FILTER_HANDLE_T *pFilterHandle;
for (bandIdx = ; bandIdx < EQ_MAX_BAND; bandIdx++)
{
pFilterHandle = &(pEqHandle->filterHandles[bandIdx]);
for (chIdx = ; chIdx < pDataInfo->chNum; chIdx++)
{
for (sampleIdx = ; 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() / ( * pCompressorParams->attackTimeMs / ));
pDrcHandle->alphaRelease = expf(-logf() / ( * pCompressorParams->releaseTimeMs / ));
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() / ( * pLimiterParams->attackTimeMs / ));
pDrcHandle->alphaRelease = expf(-logf() / ( * pLimiterParams->releaseTimeMs / ));
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() / ( * pExpanderParams->attackTimeMs / ));
pDrcHandle->alphaRelease = expf(-logf() / ( * pExpanderParams->releaseTimeMs /));
break;
case DRC_TYPE_AUTO:
break;
}
pDrcHandle->curGain = ;
pDrcHandle->curSmoothGainDb = ;
pDrcHandle->attackHoldCounter = ;
pDrcHandle->releaseHoldCounter = ;
} float sampleValueToDb(SAMPLE_INFO_T *pSampleInfo)
{
if (pSampleInfo == NULL)
return ;
if (pSampleInfo->sampleValue == )
pSampleInfo->sampleValue = ;
short maxSampleValue = (( << (pSampleInfo->bytesPerSample * )) - ) / ;
float db = * log10f((float)abs(pSampleInfo->sampleValue) / maxSampleValue);
//printf("maxSampleValue:%d, sampleValue:%d, db:%f\n", maxSampleValue, pSampleInfo->sampleValue, db);
return db;
} float drcComputeGainDb(DRC_HANDLE_T *pDrcHandle, float sampleDb)
{
if (pDrcHandle == NULL)
return ;
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\n", staticChract, sampleDb);
return staticChract - sampleDb; } float drcCompressorSmoothGain(DRC_HANDLE_T *pDrcHandle, float computeGainDb)
{
float smoothGainDb;
if (computeGainDb < pDrcHandle->curSmoothGainDb)
{
smoothGainDb = pDrcHandle->alphaAttack * pDrcHandle->curSmoothGainDb + ( - pDrcHandle->alphaAttack) * computeGainDb;
}
else
{
smoothGainDb = pDrcHandle->alphaRelease * pDrcHandle->curSmoothGainDb + ( - pDrcHandle->alphaRelease) * computeGainDb;
}
return smoothGainDb;
} float drcExpanderSmoothGain(DRC_HANDLE_T *pDrcHandle, float computeGainDb)
{
float smoothGainDb;
unsigned long holdTimeInSample = pDrcHandle->uDrcParams.expanderParams.holdTimeMs * / ;
if (pDrcHandle->attackHoldCounter >= holdTimeInSample && computeGainDb > pDrcHandle->curSmoothGainDb)
{
smoothGainDb = pDrcHandle->alphaAttack * pDrcHandle->curSmoothGainDb + ( - pDrcHandle->alphaAttack) * computeGainDb;
}
else if (pDrcHandle->attackHoldCounter < holdTimeInSample && computeGainDb > pDrcHandle->curSmoothGainDb)
{
smoothGainDb = pDrcHandle->curSmoothGainDb;
pDrcHandle->attackHoldCounter++;
pDrcHandle->releaseHoldCounter = ;
}
else if (pDrcHandle->releaseHoldCounter >= holdTimeInSample && computeGainDb <= pDrcHandle->curSmoothGainDb)
{
smoothGainDb = pDrcHandle->alphaRelease * pDrcHandle->curSmoothGainDb + ( - pDrcHandle->alphaRelease) * computeGainDb;
}
else if (pDrcHandle->releaseHoldCounter < holdTimeInSample && computeGainDb <= pDrcHandle->curSmoothGainDb)
{
smoothGainDb = pDrcHandle->curSmoothGainDb;
pDrcHandle->releaseHoldCounter++;
pDrcHandle->attackHoldCounter = ;
}
return smoothGainDb;
}
float drcSmoothGain(DRC_HANDLE_T *pDrcHandle, float computeGainDb)
{
if (pDrcHandle == NULL)
return ;
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\n",
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 = ; chIdx < pDataInfo->chNum; chIdx++)
{
for (sampleIdx = ; sampleIdx < pDataInfo->samples; sampleIdx++)
{
sampleInfo.bytesPerSample = ;
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 + ) * sizeof(float));
pFaderHandle->segNum = segNum;
float tmp;
if (pFaderHandle->faderParams.type != FADER_TYPE_CUBIC)
return;
//0~1 divide into N seg.
for (segIdx = ; segIdx < segNum + ; 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(, db/);
}
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 / ;
pFaderHandle->curGain = pFaderHandle->startGain = dbToGain(curVolumDb);
pFaderHandle->targetGain = dbToGain(curVolumDb + attuationDb);
pFaderHandle->curSample = ;
faderPrepareShape(pFaderHandle, );
printf("faderInit\n");
} 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 + ];
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\n", 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 = ; sampleIdx < pDataInfo->samples; sampleIdx++)
{
if (pFaderHandle->curSample != pFaderHandle->timeInSample)
{
faderCalGain(pFaderHandle);
pFaderHandle->curSample++;
}
for (chIdx = ; chIdx < pDataInfo->chNum; chIdx++)
{
pDataInfo->pData[chIdx][sampleIdx] *= pFaderHandle->curGain;
}
}
}
void printWaveHeader(WAVE_INFO *pWaveInfo)
{
printf("fileName:%s\n", pWaveInfo->fileName);
printf("riff chunk:\n");
printf("chunkId:%c%c%c%c\n", pWaveInfo->riffChunk.chunkId[], pWaveInfo->riffChunk.chunkId[], pWaveInfo->riffChunk.chunkId[], pWaveInfo->riffChunk.chunkId[]);
printf("chunkSize:%ld\n", pWaveInfo->riffChunk.chunkSize);
printf("format:%c%c%c%c\n", pWaveInfo->riffChunk.format[], pWaveInfo->riffChunk.format[], pWaveInfo->riffChunk.format[], pWaveInfo->riffChunk.format[]);
printf("fmt chunk:\n");
printf("chunkId:%c%c%c\n", pWaveInfo->fmtChunk.chunkId[], pWaveInfo->fmtChunk.chunkId[], pWaveInfo->fmtChunk.chunkId[]);
printf("chunkSize:%ld\n", pWaveInfo->fmtChunk.chunkSize);
printf("audioFormat:%d\n", pWaveInfo->fmtChunk.audioFormat);
printf("chNum:%d\n", pWaveInfo->fmtChunk.chNum);
printf("sampleRate:%ld\n", pWaveInfo->fmtChunk.sampleRate);
printf("byteRate:%ld\n", pWaveInfo->fmtChunk.byteRate);
printf("blockAlign:%d\n", pWaveInfo->fmtChunk.blockAlign);
printf("bitsPerSample:%d\n", pWaveInfo->fmtChunk.bitsPerSample);
printf("data chunk:\n");
printf("chunkId:%c%c%c%c\n", pWaveInfo->dataChunk.chunkId[], pWaveInfo->dataChunk.chunkId[], pWaveInfo->dataChunk.chunkId[], pWaveInfo->dataChunk.chunkId[]);
printf("chunkSize:%ld\n", 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[] = 'R';
pWaveInfo->riffChunk.chunkId[] = 'I';
pWaveInfo->riffChunk.chunkId[] = 'F';
pWaveInfo->riffChunk.chunkId[] = 'F';
pWaveInfo->riffChunk.chunkSize = ;
//strncpy(pWaveInfo->riffChunk.format, "WAVE", 4);
pWaveInfo->riffChunk.format[] = 'W';
pWaveInfo->riffChunk.format[] = 'A';
pWaveInfo->riffChunk.format[] = 'V';
pWaveInfo->riffChunk.format[] = 'E';
//strncpy(pWaveInfo->fmtChunk.chunkId, "fmt", 3);
pWaveInfo->fmtChunk.chunkId[] = 'f';
pWaveInfo->fmtChunk.chunkId[] = 'm';
pWaveInfo->fmtChunk.chunkId[] = 't';
pWaveInfo->fmtChunk.chunkId[] = ' ';
pWaveInfo->fmtChunk.chunkSize = sizeof(WAVE_FMT) - ;
pWaveInfo->fmtChunk.audioFormat = ;
pWaveInfo->fmtChunk.chNum = chNum;
pWaveInfo->fmtChunk.sampleRate = sampleRate;
pWaveInfo->fmtChunk.byteRate = sampleRate * chNum * bitsPerSample / ;
pWaveInfo->fmtChunk.blockAlign = chNum * bitsPerSample / ;
pWaveInfo->fmtChunk.bitsPerSample = bitsPerSample;
//strncpy(pWaveInfo->dataChunk.chunkId, "data", 4);
pWaveInfo->dataChunk.chunkId[] = 'd';
pWaveInfo->dataChunk.chunkId[] = 'a';
pWaveInfo->dataChunk.chunkId[] = 't';
pWaveInfo->dataChunk.chunkId[] = 'a'; pWaveInfo->dataChunk.chunkSize = ;
pWaveInfo->totalSampleNum = ;
///printWaveHeader(pWaveInfo);
} void rwRiffChunk(WAVE_INFO *pWaveInfo, unsigned char fgRead)
{
if (fgRead)
{
fread((char *)&pWaveInfo->riffChunk.chunkId, , , pWaveInfo->fp);
fread((char *)&pWaveInfo->riffChunk.chunkSize, , , pWaveInfo->fp);
fread((char *)&pWaveInfo->riffChunk.format, , , pWaveInfo->fp);
}
else
{
fwrite((char *)&pWaveInfo->riffChunk.chunkId, , , pWaveInfo->fp);
fwrite((char *)&pWaveInfo->riffChunk.chunkSize, , , pWaveInfo->fp);
fwrite((char *)&pWaveInfo->riffChunk.format, , , pWaveInfo->fp);
}
}
void rwFmtChunk(WAVE_INFO *pWaveInfo, unsigned char fgRead)
{
if (fgRead)
{
fread((char *)&pWaveInfo->fmtChunk.chunkId, , , pWaveInfo->fp);
fread((char *)&pWaveInfo->fmtChunk.chunkSize, , , pWaveInfo->fp);
fread((char *)&pWaveInfo->fmtChunk.audioFormat, , , pWaveInfo->fp);
fread((char *)&pWaveInfo->fmtChunk.chNum, , , pWaveInfo->fp);
fread((char *)&pWaveInfo->fmtChunk.sampleRate, , , pWaveInfo->fp);
fread((char *)&pWaveInfo->fmtChunk.byteRate, , , pWaveInfo->fp);
fread((char *)&pWaveInfo->fmtChunk.blockAlign, , , pWaveInfo->fp);
fread((char *)&pWaveInfo->fmtChunk.bitsPerSample, , , pWaveInfo->fp);
}
else
{
fwrite((char *)&pWaveInfo->fmtChunk.chunkId, , , pWaveInfo->fp);
fwrite((char *)&pWaveInfo->fmtChunk.chunkSize, , , pWaveInfo->fp);
fwrite((char *)&pWaveInfo->fmtChunk.audioFormat, , , pWaveInfo->fp);
fwrite((char *)&pWaveInfo->fmtChunk.chNum, , , pWaveInfo->fp);
fwrite((char *)&pWaveInfo->fmtChunk.sampleRate, , , pWaveInfo->fp);
fwrite((char *)&pWaveInfo->fmtChunk.byteRate, , , pWaveInfo->fp);
fwrite((char *)&pWaveInfo->fmtChunk.blockAlign, , , pWaveInfo->fp);
fwrite((char *)&pWaveInfo->fmtChunk.bitsPerSample, , , pWaveInfo->fp); }
}
void rwDataChunk(WAVE_INFO *pWaveInfo, unsigned char fgRead)
{
if (fgRead)
{
fread((char *)&pWaveInfo->dataChunk.chunkId, , , pWaveInfo->fp);
fread((char *)&pWaveInfo->dataChunk.chunkSize, , , pWaveInfo->fp);
}
else
{
fwrite((char *)&pWaveInfo->dataChunk.chunkId, , , pWaveInfo->fp);
fwrite((char *)&pWaveInfo->dataChunk.chunkSize, , , 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\n", errno);
return;
}
#if 0
retSize = fread((char *)&pWaveInfo->riffChunk, sizeof(WAVE_RIFF), , pWaveInfo->fp);
retSize = fread((char *)&pWaveInfo->fmtChunk, sizeof(WAVE_FMT), , pWaveInfo->fp);
retSize = fread((char *)&pWaveInfo->dataChunk, sizeof(WAVE_DATA), , pWaveInfo->fp);
#endif
rwRiffChunk(pWaveInfo, );
rwFmtChunk(pWaveInfo, );
rwDataChunk(pWaveInfo, );
pWaveInfo->pos = ftell(pWaveInfo->fp);
pWaveInfo->totalSampleNum = pWaveInfo->dataChunk.chunkSize / (pWaveInfo->fmtChunk.bitsPerSample / );
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 = ;
gPpBuf.fgEos = ;
gPpBuf.pData = (unsigned char ***)malloc(chNum * sizeof(unsigned char **));
for (chIdx = ; chIdx < chNum; chIdx++)
{
gPpBuf.pData[chIdx] = (unsigned char **)malloc(bankNum * sizeof(unsigned char *));
for (bankIdx =; bankIdx < bankNum; bankIdx++)
{
gPpBuf.pData[chIdx][bankIdx] = (unsigned char *) malloc(samplesPerBank * bytesPerSample * sizeof(unsigned char));
}
}
gPpBuf.fgInited = ;
} int sendData(unsigned char *writeBuffer, unsigned short chNum)
{
unsigned short sampleIdx, chIdx, byteIdx;
//printf("sendData, wp:%d, rp:%d\n", gPpBuf.bankWp, gPpBuf.bankRp);
if ((gPpBuf.bankWp + ) % gPpBuf.bankNum == gPpBuf.bankRp)
{
//full
return ;
}
else
{
for (sampleIdx = ; sampleIdx < PP_SAMPLES; sampleIdx++)
{
for (chIdx =; chIdx < chNum; chIdx++)
{
for (byteIdx = ; byteIdx < gPpBuf.bytesPerSample; byteIdx++)
{
gPpBuf.pData[chIdx][gPpBuf.bankWp][sampleIdx * gPpBuf.bytesPerSample + byteIdx] = writeBuffer[(chIdx + sampleIdx * chNum) * gPpBuf.bytesPerSample + byteIdx];
}
}
}
gPpBuf.bankWp = (gPpBuf.bankWp + ) % gPpBuf.bankNum;
}
return ;
} int recvData(unsigned char **readBuffer)
{
unsigned short chIdx;
//printf("recvData, wp:%d, rp:%d\n", gPpBuf.bankWp, gPpBuf.bankRp);
if (gPpBuf.bankWp == gPpBuf.bankRp)
{
//empty
return ;
}
else
{
for (chIdx = ; chIdx < gPpBuf.chNum; chIdx++)
{
memcpy(&readBuffer[chIdx][], &gPpBuf.pData[chIdx][gPpBuf.bankRp][], PP_SAMPLES * gPpBuf.bytesPerSample * sizeof(unsigned char));
}
gPpBuf.bankRp = (gPpBuf.bankRp + ) % gPpBuf.bankNum;
}
return ;
}
void *readThread(void *arg)
{
char *fileName = (char *)arg;
size_t retSize;
WAVE_INFO waveInfo;
memset(&waveInfo, , sizeof(WAVE_INFO));
unsigned long bytesPerLoop;
unsigned short loopIdx, loop;
unsigned long readCount = ;
readWaveHeader(fileName, &waveInfo);
initPpBuf(waveInfo.fmtChunk.chNum, , PP_SAMPLES, ); unsigned long readSize = READ_SAMPLES * waveInfo.fmtChunk.chNum * waveInfo.fmtChunk.bitsPerSample / ;
printf("readSize:%ld\n", readSize);
unsigned char *readBuffer = (unsigned char *)malloc(readSize * sizeof(unsigned char));
waveInfo.fp = fopen(fileName, "rb");
fseek(waveInfo.fp, waveInfo.pos, SEEK_SET);
while ()
{
retSize = fread(readBuffer, readSize, , waveInfo.fp);
if (retSize <= )
{
printf("fread fail,retSize:%d, %s, eof:%d, readCount:%ld\n", (int) retSize, strerror(errno), feof(waveInfo.fp), readCount);
gPpBuf.fgEos = ;
break;
}
else
{
bytesPerLoop = PP_SAMPLES *waveInfo.fmtChunk.chNum * waveInfo.fmtChunk.bitsPerSample / ;
loop = readSize / bytesPerLoop;
loopIdx = ;
while (loopIdx < loop)
{
if ( != sendData(readBuffer + loopIdx * bytesPerLoop, waveInfo.fmtChunk.chNum))
{
usleep();
}
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 = ;
if (pWaveInfo->fp == NULL)
{
pWaveInfo->fp = fopen(pWaveInfo->fileName, "wb");
#if 0
retSize = fwrite((char *)&pWaveInfo->riffChunk, sizeof(WAVE_RIFF), , pWaveInfo->fp);
retSize = fwrite((char *)&pWaveInfo->fmtChunk, sizeof(WAVE_FMT), , pWaveInfo->fp);
retSize = fwrite((char *)&pWaveInfo->dataChunk, sizeof(WAVE_DATA), , pWaveInfo->fp);
#endif
rwRiffChunk(pWaveInfo, );
rwFmtChunk(pWaveInfo, );
rwDataChunk(pWaveInfo, );
}
retSize = fwrite(pData, size, , pWaveInfo->fp);
pWaveInfo->totalSampleNum += (size / pWaveInfo->fmtChunk.chNum / (pWaveInfo->fmtChunk.bitsPerSample / ));
pWaveInfo->pos = ftell(pWaveInfo->fp);
} void updateWaveHeader(WAVE_INFO *pWaveInfo)
{
size_t retSize;
pWaveInfo->riffChunk.chunkSize = pWaveInfo->pos - ;
pWaveInfo->dataChunk.chunkSize = pWaveInfo->totalSampleNum * pWaveInfo->fmtChunk.chNum * pWaveInfo->fmtChunk.bitsPerSample / ;
fseek(pWaveInfo->fp, , SEEK_SET);
#if 0
retSize = fwrite((char *)&pWaveInfo->riffChunk, sizeof(WAVE_RIFF), , pWaveInfo->fp);
retSize = fwrite((char *)&pWaveInfo->fmtChunk, sizeof(WAVE_FMT), , pWaveInfo->fp);
retSize = fwrite((char *)&pWaveInfo->dataChunk, sizeof(WAVE_DATA), , pWaveInfo->fp);
#endif
rwRiffChunk(pWaveInfo, );
rwFmtChunk(pWaveInfo, );
rwDataChunk(pWaveInfo, );
fclose(pWaveInfo->fp); printWaveHeader(pWaveInfo);
}
void *ppThread(void *arg)
{
char *fileName = (char *)arg;
WAVE_INFO waveInfo;
memset(&waveInfo, , sizeof(waveInfo));
strncpy(waveInfo.fileName, fileName, strlen(fileName));
printf("out file:%s\n", waveInfo.fileName);
waveInfo.fp = NULL;
while(!gPpBuf.fgInited)
{
usleep();
}
initWaveInfo(&waveInfo, , , );
unsigned char **readBuffer = (unsigned char **)malloc(gPpBuf.chNum * sizeof(unsigned char *));
unsigned short chIdx;
for(chIdx = ; chIdx < gPpBuf.chNum; chIdx++)
{
readBuffer[chIdx] = (unsigned char *)malloc(PP_SAMPLES * gPpBuf.bytesPerSample * sizeof(unsigned char));
}
while ()
{
if ( != recvData(readBuffer))
{
if (gPpBuf.fgEos)
break;
usleep();
}
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[], PP_SAMPLES * gPpBuf.bytesPerSample, &waveInfo);
}
}
updateWaveHeader(&waveInfo);
fgEnd = ;
} int main(int argc, char **argv)
{
#if 0
WAVE_INFO inputWaveInfo, outputWaveInfo;
readWaveHeader(argv[], &inputWaveInfo);
//initWaveInfo(&outputWaveInfo, 2, 48000, 16);
#endif #if 1
pthread_t readThreadId, ppThreadId;
memset(&gPpBuf, , sizeof(PP_BUF_T));
// initPpBuf(6, 3, PP_SAMPLES, 2);
#if 0
memset(&gFaderHandle, , sizeof(FADER_HANDLE_T));
float curVolumDb = ;
float attuationDb = -;
FADER_TYPE_E type = FADER_TYPE_CUBIC;
unsigned long timeMs = ;
unsigned long sampleRate = ;
faderInit(&gFaderHandle, attuationDb, type, timeMs, sampleRate, curVolumDb);
#endif
memset(&gDrcHandle, , sizeof(DRC_HANDLE_T));
#if 0
DRC_COMPRESSOR_PARAM_T compressorParams;
compressorParams.thresholdDb = -;
compressorParams.attackTimeMs = ;
compressorParams.releaseTimeMs = ;
compressorParams.ratio = ;
drcInit(&gDrcHandle, &compressorParams, DRC_TYPE_COMPRESSOR);
#endif
#if 0
DRC_LIMITER_PARAM_T limiterParams;
limiterParams.thresholdDb = -;
limiterParams.attackTimeMs = ;
limiterParams.releaseTimeMs = ;
drcInit(&gDrcHandle, &limiterParams, DRC_TYPE_LIMITER);
#endif
#if 0
DRC_EXPANDER_PARAM_T expanderParams;
expanderParams.thresholdDb = -;
expanderParams.attackTimeMs = ;
expanderParams.releaseTimeMs = ;
expanderParams.ratio = ;
expanderParams.holdTimeMs = ;
drcInit(&gDrcHandle, &expanderParams, DRC_TYPE_EXPANDER);
#endif
#if 0
FILTER_PARAM_T filterParams;
memset(&filterParams, , sizeof(FILTER_PARAM_T));
filterParams.fs = ;
filterParams.f0 = ;
filterParams.Q = 0.707;
filterInit(&gFilterHandle, FILTER_TYPE_LPF, &filterParams);
#endif
#if 0
FILTER_PARAM_T filterParams;
memset(&filterParams, , sizeof(FILTER_PARAM_T));
filterParams.fs = ;
filterParams.f0 = ;
filterParams.Q = 0.707;
filterParams.gainDb = ;
filterInit(&gFilterHandle, FILTER_TYPE_LSF, &filterParams);
#endif
#if 0
FILTER_PARAM_T filterParams;
memset(&filterParams, , sizeof(FILTER_PARAM_T));
filterParams.fs = ;
filterParams.f0 = ;
filterParams.Q = 0.707;
filterInit(&gFilterHandle, FILTER_TYPE_HPF, &filterParams);
#endif
#if 0
FILTER_PARAM_T filterParams;
memset(&filterParams, , sizeof(FILTER_PARAM_T));
filterParams.fs = ;
filterParams.f0 = ;
filterParams.Q = 0.707;
filterParams.gainDb = ;
filterInit(&gFilterHandle, FILTER_TYPE_HSF, &filterParams);
#endif
eqInit(&gEqHandle, EQ_MODE_POP);
pthread_create(&readThreadId, NULL, readThread, argv[]);
pthread_create(&ppThreadId, NULL, ppThread, argv[]);
while(!fgEnd)
{
sleep();
}
#endif
return ;
}
EQ实现的更多相关文章
- <c:if test="value ne, eq, lt, gt,...."> 用法
类别 运算符 算术运算符 + . - . * . / (或 div )和 % (或 mod ) 关系运算符 == (或 eq ). != (或 ne ). < (或 lt ). > (或 ...
- jQuery中eq()方法用法实例
本文实例讲述了jQuery中eq()方法用法.分享给大家供大家参考.具体分析如下: 此方法能够获取匹配元素集上的相应位置索引的元素. 匹配元素集上元素的位置索引是从0开始的. 语法结构: 复制代码 代 ...
- jqueyr eq get用法
相信大部份人都会把这2个的用法搞错.仔细查看下API文档就可以知道.eq返回的是一个jquery对象,get返回的是一个html 对象数组.举个例子: <p style="color: ...
- JQ first-child与:first的区别以及nth-child(index)与eq(index)的区别
1.first-child和:first区别 first-child 是指选取每个父元素的第一个子元素 如$("div:first-child")指每个父级里的第一个div孩子 ...
- jQuery.eq() 函数
eq() 函数 获取当前对象中指定索引所对应的的元素 语法 $selector.eq(index)//index为指定索引,值为数字型 返回值 返回值为一个对象 实例说明 代码 <!DOCTYP ...
- jQuery的eq方法
定义和用法eq() 方法将匹配元素集缩减值指定 index 上的一个. 语法.eq(index) 其中的index :整数,指示元素的位置(最小为 0).如果是负数,则从集合中的最后一个元素往回计数. ...
- jQuery原型方法first,last,eq,slice源码分析
这4个方法中前3个方法很常用大家都见过,但是slice方法可能会以为是数组方法,其实slice也是jQuery的一个原型方法,只不过是底层方法是为其他方法服务的(更具体点是为eq方法服务的),首先还是 ...
- yourphp的eq作用
<eq name="> <span style="color:#090">是</span> <else /> 否 < ...
- jQuery : eq() vs get()
.get(index) and .eq(index) both return a single "element" from a jQuery object array, but ...
- JQUERY添加、删除元素、eq()方法;
一.jQuery - 添加元素 1.append() - 在被选元素内部的结尾插入指定内容 2.prepend() - 在被选元素内部的开头插入指定内容 3.after() - 在被选元素之后插入内容 ...
随机推荐
- 视频格式转换mp4
第一步:https://ffmpeg.zeranoe.com/builds/下载ffmpeg 或者:百度云下载: 链接:https://pan.baidu.com/s/1x_QogbV8xFjkYTe ...
- springboot中返回值json中null转换空字符串
在实际项目中,我们难免会遇到一些无值.当我们转JSON时,不希望这些null出现,比如我们期望所有的null在转JSON时都变成“”“”这种空字符串,那怎么做呢? Jackson中对null的处理 @ ...
- 搜索字母a或A
Amy觉得英语课实在是无聊至极,他不喜欢听老师讲课. 但是闲着也是闲着,不如做点什么吧?于是他开始数英语书里的字母a和A共出现了多少次. 费了九牛二虎之力终于数完了. 作为一名软件工程专业大学生,他觉 ...
- Codeforce 584A - Olesya and Rodion
Olesya loves numbers consisting of n digits, and Rodion only likes numbers that are divisible by t. ...
- [ZJOI2008] 骑士 - 基环树dp
一类基环树dp都是这个套路吧 随便拆掉环上的一条边 然后跑树形dp,设\(f[i][0/1]\)表示以第\(i\)个人为根的子树,第\(i\)个人选或不选,能收获的最大值 以断点\(u,v\)为根分别 ...
- java mail发送html格式的邮件
// 获取系统属性 Properties properties = System.getProperties(); // 设置邮件服务器 properties.setProperty("ma ...
- python中一些相似用法的区别:index()和find(),dict[]和get()
index和find在字符串中的区别: index()方法和find()方法相似,唯一的区别就是find方法不包含索引值会返回-1,而index()不包含索引值会抛出异常 同样的:获取字典dict ...
- arm-linux-gcc
搭建交叉编译环境,即安装.配置交叉编译工具链.在Ubuntu环境下编译出嵌入式Linux系统所需的操作系统.应用程序等,然后再上传到目标机上. 交叉编译工具链是为了编译.链接.处理和调试跨平台体系结构 ...
- api接口出现Provisional headers are shown,
问题分析:根据反馈可以知道,发起请求,但服务器未及时响应,原因可能是超时,或者被拦截
- 油候插件grant的使用
// ==UserScript== // @name Test Baidu // @namespace http://www.baidu.com/ // @version 0.1 // @descri ...