ref: ce517563012c1a25e86cea537304e9fd7c91c969
dir: /LEAF/Src/leaf-pitch.c/
/*============================================================================== leaf-pitch.c Created: 30 Nov 2018 11:02:59am Author: airship ==============================================================================*/ #if _WIN32 || _WIN64 #include "..\Inc\leaf-pitch.h" #else #include "../Inc/leaf-pitch.h" #endif static int pitchshifter_attackdetect(tPitchShifter* ps); void tPitchShifter_init(tPitchShifter* const ps, float* in, float* out, int bufSize, int frameSize) { ps->inBuffer = in; ps->outBuffer = out; ps->bufSize = bufSize; ps->frameSize = frameSize; ps->framesPerBuffer = ps->bufSize / ps->frameSize; ps->curBlock = 1; ps->lastBlock = 0; ps->index = 0; ps->hopSize = DEFHOPSIZE; ps->windowSize = DEFWINDOWSIZE; ps->fba = FBA; tEnv_init(&ps->env, ps->windowSize, ps->hopSize, ps->frameSize); tSNAC_init(&ps->snac, DEFOVERLAP); tSOLAD_init(&ps->sola); tHighpass_init(&ps->hp, HPFREQ); tSOLAD_setPitchFactor(&ps->sola, DEFPITCHRATIO); tPitchShifter_setTimeConstant(ps, DEFTIMECONSTANT); } void tPitchShifter_free(tPitchShifter* const ps) { tEnv_free(&ps->env); tSNAC_free(&ps->snac); tSOLAD_free(&ps->sola); tHighpass_free(&ps->hp); leaf_free(ps); } float tPitchShifter_tick(tPitchShifter* ps, float sample) { float period, out; int i, iLast; i = (ps->curBlock*ps->frameSize); iLast = (ps->lastBlock*ps->frameSize)+ps->index; out = tHighpass_tick(&ps->hp, ps->outBuffer[iLast]); ps->inBuffer[i+ps->index] = sample; ps->index++; if (ps->index >= ps->frameSize) { ps->index = 0; tEnv_processBlock(&ps->env, &(ps->inBuffer[i])); if(pitchshifter_attackdetect(ps) == 1) { ps->fba = 5; tSOLAD_setReadLag(&ps->sola, ps->windowSize); } tSNAC_ioSamples(&ps->snac, &(ps->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); period = tSNAC_getPeriod(&ps->snac); ps->curBlock++; if (ps->curBlock >= ps->framesPerBuffer) ps->curBlock = 0; ps->lastBlock++; if (ps->lastBlock >= ps->framesPerBuffer) ps->lastBlock = 0; //separate here tSOLAD_setPeriod(&ps->sola, period); tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); tSOLAD_ioSamples(&ps->sola, &(ps->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); } return out; } float tPitchShifterToFreq_tick(tPitchShifter* ps, float sample, float freq) { float period, out; int i, iLast; i = (ps->curBlock*ps->frameSize); iLast = (ps->lastBlock*ps->frameSize)+ps->index; out = tHighpass_tick(&ps->hp, ps->outBuffer[iLast]); ps->inBuffer[i+ps->index] = sample; ps->index++; if (ps->index >= ps->frameSize) { ps->index = 0; tEnv_processBlock(&ps->env, &(ps->inBuffer[i])); if(pitchshifter_attackdetect(ps) == 1) { ps->fba = 5; tSOLAD_setReadLag(&ps->sola, ps->windowSize); } tSNAC_ioSamples(&ps->snac, &(ps->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); period = tSNAC_getPeriod(&ps->snac); tSOLAD_setPeriod(&ps->sola, period); ps->pitchFactor = period*freq*leaf.invSampleRate; tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); tSOLAD_ioSamples(&ps->sola, &(ps->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); ps->curBlock++; if (ps->curBlock >= ps->framesPerBuffer) ps->curBlock = 0; ps->lastBlock++; if (ps->lastBlock >= ps->framesPerBuffer) ps->lastBlock = 0; } return out; } float tPitchShifterToFunc_tick(tPitchShifter* ps, float sample, float (*fun)(float)) { float period, out; int i, iLast; i = (ps->curBlock*ps->frameSize); iLast = (ps->lastBlock*ps->frameSize)+ps->index; out = tHighpass_tick(&ps->hp, ps->outBuffer[iLast]); ps->inBuffer[i+ps->index] = sample; ps->index++; if (ps->index >= ps->frameSize) { ps->index = 0; tEnv_processBlock(&ps->env, &(ps->inBuffer[i])); if(pitchshifter_attackdetect(ps) == 1) { ps->fba = 5; tSOLAD_setReadLag(&ps->sola, ps->windowSize); } tSNAC_ioSamples(&ps->snac, (&ps->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); period = tSNAC_getPeriod(&ps->snac); tSOLAD_setPeriod(&ps->sola, period); ps->pitchFactor = period/fun(period); tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); tSOLAD_ioSamples(&ps->sola, &(ps->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); ps->curBlock++; if (ps->curBlock >= ps->framesPerBuffer) ps->curBlock = 0; ps->lastBlock++; if (ps->lastBlock >= ps->framesPerBuffer) ps->lastBlock = 0; } return out; } void tPitchShifter_ioSamples(tPitchShifter* ps, float* in, float* out, int size) { float period; tEnv_processBlock(&ps->env, in); if(pitchshifter_attackdetect(ps) == 1) { ps->fba = 5; tSOLAD_setReadLag(&ps->sola, ps->windowSize); } tSNAC_ioSamples(&ps->snac, in, out, size); period = tSNAC_getPeriod(&ps->snac); tSOLAD_setPeriod(&ps->sola, period); tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); tSOLAD_ioSamples(&ps->sola, in, out, size); for (int cc = 0; cc < size; ++cc) { out[cc] = tHighpass_tick(&ps->hp, out[cc]); } } void tPitchShifter_ioSamples_toFreq(tPitchShifter* ps, float* in, float* out, int size, float toFreq) { float period; tEnv_processBlock(&ps->env, in); if(pitchshifter_attackdetect(ps) == 1) { ps->fba = 5; tSOLAD_setReadLag(&ps->sola, ps->windowSize); } tSNAC_ioSamples(&ps->snac, in, out, size); period = tSNAC_getPeriod(&ps->snac); tSOLAD_setPeriod(&ps->sola, period); ps->pitchFactor = period*toFreq; tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); tSOLAD_ioSamples(&ps->sola, in, out, size); for (int cc = 0; cc < size; ++cc) { out[cc] = tHighpass_tick(&ps->hp, out[cc]); } } void tPitchShifter_ioSamples_toPeriod(tPitchShifter* ps, float* in, float* out, int size, float toPeriod) { float period; tEnv_processBlock(&ps->env, in); if(pitchshifter_attackdetect(ps) == 1) { ps->fba = 5; tSOLAD_setReadLag(&ps->sola, ps->windowSize); } tSNAC_ioSamples(&ps->snac, in, out, size); period = tSNAC_getPeriod(&ps->snac); tSOLAD_setPeriod(&ps->sola, period); ps->pitchFactor = period/toPeriod; tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); tSOLAD_ioSamples(&ps->sola, in, out, size); for (int cc = 0; cc < size; ++cc) { out[cc] = tHighpass_tick(&ps->hp, out[cc]); } } void tPitchShifter_ioSamples_toFunc(tPitchShifter* ps, float* in, float* out, int size, float (*fun)(float)) { float period; tEnv_processBlock(&ps->env, in); if(pitchshifter_attackdetect(ps) == 1) { ps->fba = 5; tSOLAD_setReadLag(&ps->sola, ps->windowSize); } tSNAC_ioSamples(&ps->snac, in, out, size); period = tSNAC_getPeriod(&ps->snac); tSOLAD_setPeriod(&ps->sola, period); ps->pitchFactor = period/fun(period); tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); tSOLAD_ioSamples(&ps->sola, in, out, size); for (int cc = 0; cc < size; ++cc) { out[cc] = tHighpass_tick(&ps->hp, out[cc]); } } void tPitchShifter_setPitchFactor(tPitchShifter* ps, float pf) { ps->pitchFactor = pf; } void tPitchShifter_setTimeConstant(tPitchShifter* ps, float tc) { ps->timeConstant = tc; ps->radius = expf(-1000.0f * ps->hopSize * leaf.invSampleRate / ps->timeConstant); } void tPitchShifter_setHopSize(tPitchShifter* ps, int hs) { ps->hopSize = hs; } void tPitchShifter_setWindowSize(tPitchShifter* ps, int ws) { ps->windowSize = ws; } float tPitchShifter_getPeriod(tPitchShifter* ps) { return tSNAC_getPeriod(&ps->snac); } static int pitchshifter_attackdetect(tPitchShifter* ps) { float envout; envout = tEnv_tick(&ps->env); if (envout >= 1.0f) { ps->lastmax = ps->max; if (envout > ps->max) { ps->max = envout; } else { ps->deltamax = envout - ps->max; ps->max = ps->max * ps->radius; } ps->deltamax = ps->max - ps->lastmax; } ps->fba = ps->fba ? (ps->fba - 1) : 0; return (ps->fba == 0 && (ps->max > 60 && ps->deltamax > 6)) ? 1 : 0; } void tPeriod_init (tPeriod* const p, float* in, float* out, int bufSize, int frameSize) { p->inBuffer = in; p->outBuffer = out; p->bufSize = bufSize; p->frameSize = frameSize; p->framesPerBuffer = p->bufSize / p->frameSize; p->curBlock = 1; p->lastBlock = 0; p->index = 0; p->hopSize = DEFHOPSIZE; p->windowSize = DEFWINDOWSIZE; p->fba = FBA; tEnv_init(&p->env, p->windowSize, p->hopSize, p->frameSize); tSNAC_init(&p->snac, DEFOVERLAP); p->timeConstant = DEFTIMECONSTANT; p->radius = expf(-1000.0f * p->hopSize * leaf.invSampleRate / p->timeConstant); } void tPeriod_free (tPeriod* const p) { tEnv_free(&p->env); tSNAC_free(&p->snac); leaf_free(p); } float tPeriod_findPeriod (tPeriod* p, float sample) { float period; int i, iLast; i = (p->curBlock*p->frameSize); iLast = (p->lastBlock*p->frameSize)+p->index; p->i = i; p->iLast = iLast; p->inBuffer[i+p->index] = sample; p->index++; p->indexstore = p->index; if (p->index >= p->frameSize) { p->index = 0; tEnv_processBlock(&p->env, &(p->inBuffer[i])); tSNAC_ioSamples(&p->snac, &(p->inBuffer[i]), &(p->outBuffer[i]), p->frameSize); p->period = tSNAC_getPeriod(&p->snac); p->curBlock++; if (p->curBlock >= p->framesPerBuffer) p->curBlock = 0; p->lastBlock++; if (p->lastBlock >= p->framesPerBuffer) p->lastBlock = 0; } // changed from period to p->period return p->period; } void tPeriod_setHopSize(tPeriod* p, int hs) { p->hopSize = hs; } void tPeriod_setWindowSize(tPeriod* p, int ws) { p->windowSize = ws; } void tPitchShift_setPitchFactor(tPitchShift* ps, float pf) { ps->pitchFactor = pf; } static int pitchshift_attackdetect(tPitchShift* ps) { float envout; envout = tEnv_tick(&ps->p->env); if (envout >= 1.0f) { ps->p->lastmax = ps->p->max; if (envout > ps->p->max) { ps->p->max = envout; } else { ps->p->deltamax = envout - ps->p->max; ps->p->max = ps->p->max * ps->radius; } ps->p->deltamax = ps->p->max - ps->p->lastmax; } ps->p->fba = ps->p->fba ? (ps->p->fba - 1) : 0; return (ps->p->fba == 0 && (ps->p->max > 60 && ps->p->deltamax > 6)) ? 1 : 0; } void tPitchShift_init (tPitchShift* const ps, tPeriod* p, float* out, int bufSize) { ps->p = p; ps->outBuffer = out; ps->bufSize = bufSize; ps->frameSize = p->frameSize; ps->framesPerBuffer = ps->bufSize / ps->frameSize; ps->curBlock = 1; ps->lastBlock = 0; ps->index = 0; ps->pitchFactor = 1.0f; tSOLAD_init(&ps->sola); tHighpass_init(&ps->hp, HPFREQ); tSOLAD_setPitchFactor(&ps->sola, DEFPITCHRATIO); } void tPitchShift_free(tPitchShift* const ps) { tSOLAD_free(&ps->sola); tHighpass_free(&ps->hp); leaf_free(ps); } float tPitchShift_shift (tPitchShift* ps) { float period, out; int i, iLast; i = ps->p->i; iLast = ps->p->iLast; out = tHighpass_tick(&ps->hp, ps->outBuffer[iLast]); if (ps->p->indexstore >= ps->frameSize) { period = ps->p->period; if(pitchshift_attackdetect(ps) == 1) { ps->p->fba = 5; tSOLAD_setReadLag(&ps->sola, ps->p->windowSize); } tSOLAD_setPeriod(&ps->sola, period); tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); tSOLAD_ioSamples(&ps->sola, &(ps->p->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); } return out; } float tPitchShift_shiftToFreq (tPitchShift* ps, float freq) { float period, out; int i, iLast; i = ps->p->i; iLast = ps->p->iLast; out = tHighpass_tick(&ps->hp, ps->outBuffer[iLast]); if (ps->p->indexstore >= ps->frameSize) { period = ps->p->period; if(pitchshift_attackdetect(ps) == 1) { ps->p->fba = 5; tSOLAD_setReadLag(&ps->sola, ps->p->windowSize); } tSOLAD_setPeriod(&ps->sola, period); ps->pitchFactor = period*freq*leaf.invSampleRate; tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); tSOLAD_ioSamples(&ps->sola, &(ps->p->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); } return out; } float tPitchShift_shiftToFunc (tPitchShift* ps, float (*fun)(float)) { float period, out; int i, iLast; i = ps->p->i; iLast = ps->p->iLast; out = tHighpass_tick(&ps->hp, ps->outBuffer[iLast]); if (ps->p->indexstore >= ps->frameSize) { period = ps->p->period; if(pitchshift_attackdetect(ps) == 1) { ps->p->fba = 5; tSOLAD_setReadLag(&ps->sola, ps->p->windowSize); } tSOLAD_setPeriod(&ps->sola, period); ps->pitchFactor = period/fun(period); tSOLAD_setPitchFactor(&ps->sola, ps->pitchFactor); tSOLAD_ioSamples(&ps->sola, &(ps->p->inBuffer[i]), &(ps->outBuffer[i]), ps->frameSize); ps->curBlock++; if (ps->curBlock >= ps->p->framesPerBuffer) ps->curBlock = 0; ps->lastBlock++; if (ps->lastBlock >= ps->framesPerBuffer) ps->lastBlock = 0; } return out; }