ref: bc99f48aca15efd07cb81853172b95e83076314f
dir: /LEAF/Src/leaf-sampling.c/
/* ============================================================================== leaf-sampling.c Created: 20 Jan 2017 12:02:17pm Author: Michael R Mulshine ============================================================================== */ #if _WIN32 || _WIN64 #include "..\Inc\leaf-sampling.h" #include "..\leaf.h" #else #include "../Inc/leaf-sampling.h" #include "../leaf.h" #endif //============================================================================== void tBuffer_init (tBuffer* const sb, uint32_t length) { _tBuffer* s = *sb = (_tBuffer*) leaf_alloc(sizeof(_tBuffer)); s->buff = (float*) leaf_alloc( sizeof(float) * length); s->bufferLength = length; s->recordedLength = 0; s->active = 0; s->idx = 0; s->mode = RecordOneShot; tBuffer_clear(sb); } void tBuffer_init_locate (tBuffer* const sb, uint32_t length, mpool_t* pool) { _tBuffer* s = *sb = (_tBuffer*) mpool_alloc(sizeof(_tBuffer), pool); s->buff = (float*) mpool_alloc( sizeof(float) * length, pool); s->bufferLength = length; s->recordedLength = 0; s->idx = 0; s->mode = RecordOneShot; tBuffer_clear(sb); } void tBuffer_free (tBuffer* const sb) { _tBuffer* s = *sb; leaf_free(s->buff); leaf_free(s); } void tBuffer_free_locate (tBuffer* const sb, mpool_t* pool) { _tBuffer* s = *sb; mpool_free(s->buff, pool); mpool_free(s, pool); } void tBuffer_tick (tBuffer* const sb, float sample) { _tBuffer* s = *sb; if (s->active == 1) { s->buff[s->idx] = sample; s->idx += 1; if (s->idx >= s->bufferLength) { if (s->mode == RecordOneShot) { tBuffer_stop(sb); } else if (s->mode == RecordLoop) { s->idx = 0; } } } } void tBuffer_read(tBuffer* const sb, float* buff, uint32_t len) { _tBuffer* s = *sb; for (int i = 0; i < s->bufferLength; i++) { if (i < len) s->buff[i] = buff[i]; else s->buff[i] = 0.f; } s->recordedLength = len; } float tBuffer_get (tBuffer* const sb, int idx) { _tBuffer* s = *sb; if ((idx < 0) || (idx >= s->bufferLength)) return 0.f; return s->buff[idx]; } void tBuffer_record(tBuffer* const sb) { _tBuffer* s = *sb; s->active = 1; s->idx = 0; } void tBuffer_stop(tBuffer* const sb) { _tBuffer* s = *sb; s->active = 0; s->recordedLength = s->idx; } int tBuffer_getRecordPosition(tBuffer* const sb) { _tBuffer* s = *sb; return s->idx; } void tBuffer_setRecordMode (tBuffer* const sb, RecordMode mode) { _tBuffer* s = *sb; s->mode = mode; } void tBuffer_clear (tBuffer* const sb) { _tBuffer* s = *sb; for (int i = 0; i < s->bufferLength; i++) { s->buff[i] = 0.f; } } uint32_t tBuffer_getBufferLength(tBuffer* const sb) { _tBuffer* s = *sb; return s->bufferLength; } uint32_t tBuffer_getRecordedLength(tBuffer* const sb) { _tBuffer* s = *sb; return s->recordedLength; } //================================tSampler===================================== static void handleStartEndChange(tSampler* const sp); static void attemptStartEndChange(tSampler* const sp); void tSampler_init(tSampler* const sp, tBuffer* const b) { _tSampler* p = *sp = (_tSampler*) leaf_alloc(sizeof(_tSampler)); _tBuffer* s = *b; p->samp = s; p->active = 0; p->start = 0; p->end = p->samp->bufferLength - 1; p->len = p->end - p->start; p->idx = 0.f; p->inc = 1.f; p->iinc = 1.f; p->dir = 1; p->flip = 1; p->bnf = 1; p->mode = PlayNormal; p->cfxlen = 500; // default 300 sample crossfade tRamp_init(&p->gain, 7.0f, 1); tRamp_setVal(&p->gain, 0.f); p->targetstart = -1; p->targetend = -1; } void tSampler_free (tSampler* const sp) { _tSampler* p = *sp; tRamp_free(&p->gain); leaf_free(p); } void tSampler_setSample (tSampler* const sp, tBuffer* const b) { _tSampler* p = *sp; _tBuffer* s = *b; p->samp = s; p->start = 0; p->end = p->samp->bufferLength - 1; p->len = p->end - p->start; p->idx = 0.f; } volatile uint32_t errorState = 0; float tSampler_tick (tSampler* const sp) { _tSampler* p = *sp; if (p->active == 0) return 0.f; if ((p->inc == 0.0f) || (p->len == 0)) { return p->last; } attemptStartEndChange(sp); float sample = 0.f; float cfxsample = 0.f; float numticks; float g1 = 1.f, g2 = 0.f; float* buff = p->samp->buff; int idx, revidx; float alpha, revalpha; int32_t start = p->start; int32_t end = p->end; if (p->flip < 0) { start = p->end; end = p->start; } int dir = p->bnf * p->dir * p->flip; idx = (int) p->idx; alpha = p->idx - idx; revidx = idx + 1;// we think this is because flooring on int works different when reading backwards revalpha = 1.f - alpha; uint32_t cfxlen = p->cfxlen; if (p->len < cfxlen) cfxlen = p->len * 0.25f;//p->len; int length = p->samp->recordedLength; if (dir > 0) { // num samples (hopping the increment size) to end of loop numticks = (end-idx) * p->iinc; } else { numticks = (revidx-start) * p->iinc; } // Check dir (direction) to interpolate properly if (dir > 0) { // FORWARD NORMAL SAMPLE int i1 = ((idx-1) < 0) ? 0 : idx-1; int i3 = ((idx+1) >= length) ? (idx) : (idx+1); int i4 = ((idx+2) >= length) ? (length-1) : (idx+2); sample = LEAF_interpolate_hermite_x (buff[i1], buff[idx], buff[i3], buff[i4], alpha); if (cfxlen > 0)// necessary to avoid divide by zero, also a waste of computation otherwise { if (p->mode == PlayLoop) { if (numticks <= (float) cfxlen) { // CROSSFADE SAMPLE int cdx = start - (numticks * p->inc); if (cdx < 1) { cdx = -cdx; i1 = ((cdx+1) >= length) ? (length-1) : cdx+1; i3 = ((cdx-1) < 0) ? cdx : (cdx-1); i4 = ((cdx-2) < 0) ? 0 : (cdx-2); cfxsample = LEAF_interpolate_hermite_x (buff[i1], buff[cdx], buff[i3], buff[i4], revalpha); } else { i1 = ((cdx-1) < 0) ? 0 : cdx-1; i3 = ((cdx+1) >= length) ? (cdx) : (cdx+1); i4 = ((cdx+2) >= length) ? (length-1) : (cdx+2); cfxsample = LEAF_interpolate_hermite_x (buff[i1], buff[cdx], buff[i3], buff[i4], alpha); } g2 = (float) (cfxlen - numticks) / (float) cfxlen; } } } else { g2 = 0.0f; } } else { // REVERSE int i1 = ((revidx+1) >= length) ? (length-1) : revidx+1; int i3 = ((revidx-1) < 0) ? revidx : (revidx-1); int i4 = ((revidx-2) < 0) ? 0 : (revidx-2); sample = LEAF_interpolate_hermite_x (buff[i1], buff[revidx], buff[i3], buff[i4], revalpha); if (cfxlen > 0)// necessary to avoid divide by zero, also a waste of computation otherwise { if (p->mode == PlayLoop) { if (numticks <= (float) cfxlen) { // CROSSFADE SAMPLE int cdx = end + (numticks * p->inc); if (cdx > length - 2) { //the problem with the click is here --- at some point it crosses this threshold and jumps from a point near the boundary to a point far away from the boundary - that's not correct ///// ooooops cdx = end - (numticks * p->inc); i1 = ((cdx-1) < 0) ? 0 : cdx-1; i3 = ((cdx+1) >= length) ? (cdx) : (cdx+1); i4 = ((cdx+2) >= length) ? (length-1) : (cdx+2); cfxsample = LEAF_interpolate_hermite_x (buff[i1], buff[cdx], buff[i3], buff[i4], revalpha); } else { i1 = ((cdx+1) >= length) ? (length-1) : cdx+1; i3 = ((cdx-1) < 0) ? cdx : (cdx-1); i4 = ((cdx-2) < 0) ? 0 : (cdx-2); cfxsample = LEAF_interpolate_hermite_x (buff[i1], buff[cdx], buff[i3], buff[i4], alpha); } g2 = (float) (cfxlen - numticks) / (float) cfxlen; } } } else { g2 = 0.0f; } } float inc = fmod(p->inc, p->len); p->idx += (dir * inc); //handle start and end cases for looping and back and forth modes if (p->mode == PlayLoop) { while((int)p->idx < start) { p->idx += (float)(p->len); } while((int)p->idx > end) { p->idx -= (float)(p->len); } } else // == PlayBackAndForth { if (p->idx < start) { p->bnf = -p->bnf; p->idx = start + 1; } else if (p->idx > end) { p->bnf = -p->bnf; p->idx = end - 1; } } //handle very short fade out for end of one-shot normal playback if (p->mode == PlayNormal) { if (numticks < (0.007f * leaf.sampleRate)) { tRamp_setDest(&p->gain, 0.f); p->active = -1; } } g1 = 1.f - g2; sample = sample * g1 + cfxsample * g2; sample = sample * tRamp_tick(&p->gain); if (p->active < 0) { if (tRamp_sample(&p->gain) <= 0.00001f) { if (p->retrigger == 1) { p->active = 1; p->retrigger = 0; tRamp_setDest(&p->gain, 1.f); if (p->dir > 0) { if (p->flip > 0) p->idx = p->start; else p->idx = p->end; } else { if (p->flip > 0) p->idx = p->end; else p->idx = p->start; } } else { p->active = 0; } } } if (fabsf(sample-p->last) > 0.1f) { errorState = 1; } p->last = sample; return p->last; } void tSampler_setMode (tSampler* const sp, PlayMode mode) { _tSampler* p = *sp; p->mode = mode; } void tSampler_setCrossfadeLength (tSampler* const sp, uint32_t length) { _tSampler* p = *sp; uint32_t cfxlen = LEAF_clip(0, length, 1000); if (cfxlen > (p->len * 0.25f)) cfxlen = p->len * 0.25f; p->cfxlen = cfxlen; } void tSampler_play (tSampler* const sp) { _tSampler* p = *sp; if (p->active != 0) { p->active = -1; p->retrigger = 1; tRamp_setDest(&p->gain, 0.f); } else { p->active = 1; p->retrigger = 0; tRamp_setDest(&p->gain, 1.f); if (p->dir > 0) { if (p->flip > 0) p->idx = p->start; else p->idx = p->end; } else { if (p->flip > 0) p->idx = p->end; else p->idx = p->start; } handleStartEndChange(&p); } } void tSampler_stop (tSampler* const sp) { _tSampler* p = *sp; p->active = -1; tRamp_setDest(&p->gain, 0.f); } static void handleStartEndChange(tSampler* const sp) { _tSampler* p = *sp; p->len = abs(p->end - p->start); if (p->len < (p->cfxlen * 0.25f)) p->cfxlen = p->len * 0.25f; if (p->start > p->end) { p->flip = -1; } else { p->flip = 1; } } static void attemptStartEndChange(tSampler* const sp) { _tSampler* p = *sp; // Try to update start/end if needed if (p->targetstart >= 0) { tSampler_setStart(sp, p->targetstart); } if (p->targetend >= 0) { tSampler_setEnd(sp, p->targetend); } } void tSampler_setStart (tSampler* const sp, int32_t start) { _tSampler* p = *sp; int tempflip; if (start == p->end) { return; } // if (p->active) { if (start > p->end) { tempflip = -1; } else { tempflip = 1; } int dir = p->bnf * p->dir * tempflip; uint32_t cfxlen = (p->len < p->cfxlen) ? 0 : p->cfxlen; if ((tempflip > 0) && (dir > 0)) // start is start and we're playing forward { if (((start > p->idx) || (p->end-p->idx <= cfxlen)) && (start > p->end))// start given is after current index or we're in a crossfade { p->targetstart = start; return; } } else if ((tempflip < 0) && (dir < 0)) // start is end and we're playing in reverse { if (((start < p->idx) || (p->idx-p->end <= cfxlen)) && (start < p->end))// start given is before current index or we're in a crossfade { p->targetstart = start; return; } } } p->start = LEAF_clipInt(0, start, p->samp->recordedLength - 1); handleStartEndChange(sp); p->targetstart = -1; } void tSampler_setEnd (tSampler* const sp, int32_t end) { _tSampler* p = *sp; int tempflip; if (end == p->start) { return; } //if (p->active) { if (p->start > end) { tempflip = -1; } else { tempflip = 1; } int dir = p->bnf * p->dir * tempflip; uint32_t cfxlen = (p->len < p->cfxlen) ? 0 : p->cfxlen; if (tempflip > 0 && dir < 0) // end is end and we're playing in reverse { if (((end < p->idx) || (p->idx-p->start <= cfxlen)) && (end < p->start)) // end given is before current index or we're in a crossfade { p->targetend = end; return; } } else if (tempflip < 0 && dir > 0) // end is start and we're playing forward { if (((end > p->idx) || (p->start-p->idx <= cfxlen)) && (end > p->start)) // end given is after current index or we're in a crossfade { p->targetend = end; return; } } } p->end = LEAF_clipInt(0, end, (p->samp->recordedLength - 1)); handleStartEndChange(sp); p->targetend = -1; } void tSampler_setRate (tSampler* const sp, float rate) { _tSampler* p = *sp; if (rate < 0.f) { rate = -rate; p->dir = -1; } else { p->dir = 1; } p->inc = rate; p->iinc = 1.f / p->inc; } //==============================================================================