public Recording(FPCM buffer, int offset) { this.buffer = buffer; this.cachedStart = offset; this.length = this.buffer.buffer.Length; this.cachedEnd = offset + this.length; }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBufSz, FPCMFactoryGenLimit pcmFactory) { FPCM fa = pcmFactory.GetZeroedFPCM(start, size); float [] a = fa.buffer; fixed(float *pa = a) { this.input.Accumulate(pa, start, size, prefBufSz, pcmFactory); for (int i = start; i < start + size; ++i) { float s = pa[i]; if (s == 0.0) { data[i] = 0.0f; } else if (s > 0.0) { data[i] = 1.0f; } else { data[i] = 0.0f; } } } }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBuffSz, FPCMFactoryGenLimit pcmFactory) { if (this.batch.Length == 0) { return; } float inv = 1.0f / this.batch.Length; FPCM fpcm = null; foreach (GenBase gb in this.batch) { if (fpcm == null) { fpcm = pcmFactory.GetZeroedFPCM(start, size); } else { fpcm.Zero(start, size); } float [] rf = fpcm.buffer; fixed(float *prf = rf) { gb.Accumulate(prf, start, size, prefBuffSz, pcmFactory); for (int i = start; i < start + size; ++i) { data[i] += prf[i] * inv; } } } }
public FPCM GetFPCM(int samples) { if (samples < 1) { return(null); } List <FPCM> lst; if (this.entries.TryGetValue(samples, out lst) == false) { FPCM newRet = new FPCM(this, new float[samples]); return(newRet); } int lastIdx = lst.Count - 1; FPCM ret = lst[lastIdx]; lst.RemoveAt(lastIdx); if (lst.Count == 0) { this.entries.Remove(samples); } return(ret); }
FPCM IFPCMFactory.GetZeroedGlobalFPCM(int samples, int start, int size) { FPCM fpcm = this.GetFPCM(samples); fpcm.Zero(start, size); return(fpcm); }
public BufferedFPCM(FPCM fpcm, int offset) { this.buffer = fpcm; this.offset = offset; this.cachedLen = fpcm.buffer.Length; this.cachedEnd = offset + this.cachedLen; this.readLeft = this.cachedLen; }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBuffSz, FPCMFactoryGenLimit pcmFactory) { if (this.passed == true) { this.gen.Accumulate(data, start, size, prefBuffSz, pcmFactory); return; } if (this.offset > 0) { int burn = Min(size, this.offset); start += burn; size -= burn; this.offset -= burn; if (size <= 0) { return; } } FPCM fa = pcmFactory.GetZeroedFPCM(start, size); float [] a = fa.buffer; fixed(float *pa = a) { this.gen.Accumulate(pa, start, size, prefBuffSz, pcmFactory); float at = this.itAttack; float tot = (float)this.totalAttackSamples; int sampCt = Min(size, totalAttackSamples - itAttack); for (int i = start; i < start + sampCt; ++i) { float v = at / tot; at += 1.0f; data[i] = pa[i] * v; } start += sampCt; size -= sampCt; this.itAttack += sampCt; if (this.itAttack >= this.totalAttackSamples) { this.passed = true; } for (int i = start; i < start + size; ++i) { data[i] = pa[i]; } } }
FPCM IFPCMFactory.GetGlobalFPCM(int samples, bool zero) { FPCM fpcm = this.GetFPCM(samples); if (zero == true) { fpcm.Zero(); } return(fpcm); }
FPCM IFPCMFactory.GetFPCM(int samples, bool zero) { FPCM ret = this.parent.GetFPCM(samples, zero); if (ret == null) { return(null); } this.allocated.Add(ret); return(ret); }
FPCM IFPCMFactory.GetZeroedFPCM(int samples, int start, int size) { FPCM ret = this.parent.GetZeroedFPCM(samples, start, size); if (ret == null) { return(null); } this.allocated.Add(ret); return(ret); }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBuffSz, FPCMFactoryGenLimit pcmFactory) { FPCM fa = pcmFactory.GetZeroedFPCM(start, size); float [] a = fa.buffer; fixed(float *pa = a) { this.input.Accumulate(pa, start, size, prefBuffSz, pcmFactory); for (int i = start; i < start + size; ++i) { data[i] = ((float)(int)(pa[i] * factor)) * invFactor; } } }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBuffSz, FPCMFactoryGenLimit pcmFactory) { while (size > 0) { if (this.playing == true) { int burn = Min(size, this.samplesLeft); this.input.Accumulate(data, start, size, prefBuffSz, pcmFactory); this.samplesLeft -= burn; size -= burn; start += burn; if (this.samplesLeft == 0) { this.playing = false; this.samplesLeft = this.holdSamples; } } else { int burn = Min(size, this.samplesLeft); // A true burn, pulling the samples just to do absolutely nothing // with them, but we need to get time to pass for the rest of the hierarchy. FPCM fa = pcmFactory.GetZeroedFPCM(start, size); float[] a = fa.buffer; fixed(float *pa = a) { this.input.Accumulate(pa, start, size, prefBuffSz, pcmFactory); } this.samplesLeft -= burn; size -= burn; start += burn; if (this.samplesLeft == 0) { this.playing = true; this.samplesLeft = this.playSamples; } } } }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBuffSz, FPCMFactoryGenLimit pcmFactory) { if (this.active == false) { // If not ready, just relay as if we weren't here this.input.Accumulate(data, start, size, prefBuffSz, pcmFactory); return; } // if we've passed the end of release, we're still going to add silent // data for a while and record for how long we've been doing that. // // See this.Finished() for more information. if (this.curLen <= 0) { this.curLen -= size; return; } FPCM fa = pcmFactory.GetZeroedFPCM(start, size); float[] a = fa.buffer; fixed(float *pa = a) { this.input.Accumulate(pa, start, size, prefBuffSz, pcmFactory); float c = this.curLen; int sz = Mathf.Min(this.curLen, size); this.curLen -= sz; float fmax = (float)this.maxLen; for (int i = start; i < start + sz; ++i) { c -= 1.0f; float lam = c / fmax; lam = lam * lam; data[i] = lam * pa[i]; } } }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBuffSz, FPCMFactoryGenLimit pcmFactory) { FPCM fa = pcmFactory.GetZeroedFPCM(start, size); FPCM fb = pcmFactory.GetZeroedFPCM(start, size); float [] a = fa.buffer; float [] b = fb.buffer; fixed(float *pa = a, pb = b) { this.gma.Accumulate(pa, start, size, prefBuffSz, pcmFactory); this.gmb.Accumulate(pb, start, size, prefBuffSz, pcmFactory); for (int i = start; i < start + size; ++i) { data[i] = pa[i] + pb[i]; } } }
/// <summary> /// Add an FPCM back into the pool /// </summary> /// <param name="fpcm">The FPCM to add back.</param> /// <returns>If true, the FPCM was successfully added.</returns> public bool ReturnFPCM(FPCM fpcm) { if (fpcm.buffer == null || fpcm.buffer.Length == 0) { return(false); } int samples = fpcm.buffer.Length; List <FPCM> lst; if (this.entries.TryGetValue(samples, out lst) == false) { lst = new List <FPCM>(); this.entries.Add(samples, lst); } lst.Add(fpcm); return(true); }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBufSz, FPCMFactoryGenLimit pcmFactory) { // Early release if (this.released == true) { if (this.releaseLeft > 0) { FPCM fpcmRl = pcmFactory.GetZeroedFPCM(start, size); float[] fprl = fpcmRl.buffer; int sampsCt = Mathf.Min(this.releaseLeft, size); // We could probably optimize this by only calling AccumulateImpl_NonReleasePart() // when we're not in sustain yet. fixed(float *pfprl = fprl) { this.AccumulateImpl_NonReleasePart(pfprl, start, size, prefBufSz, pcmFactory); float total = this.releaseTotal; float left = this.releaseLeft; for (int ei = start; ei < start + sampsCt; ++ei) { data[ei] = pfprl[ei] * (left / total); left -= 1.0f; } } this.releaseLeft -= sampsCt; return; } else if (this.saftey > 0) { this.saftey -= size; } } else { this.AccumulateImpl_NonReleasePart(data, start, size, prefBufSz, pcmFactory); } }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBuffSz, FPCMFactoryGenLimit pcmFactory) { FPCM fa = pcmFactory.GetZeroedFPCM(start, size); float[] a = fa.buffer; fixed(float *pa = a) { this.input.Accumulate(pa, start, size, prefBuffSz, pcmFactory); for (int i = start; i < start + size; ++i) { this.valAccum += this.integrateFactor * pa[i]; this.wtAccum += this.integrateFactor; data[i] += this.valAccum / this.wtAccum; this.wtAccum *= this.decayFactor; this.valAccum *= this.decayFactor; } } }
unsafe public void AccumulateImpl_NonReleasePart(float *data, int start, int size, int prefBufSz, FPCMFactoryGenLimit pcmFactory) { FPCM fpcm = pcmFactory.GetZeroedFPCM(start, size); float[] fp = fpcm.buffer; fixed(float *pfp = fp) { this.input.Accumulate(pfp, start, size, prefBufSz, pcmFactory); if (this.offset > 0) { int ofrm = Mathf.Min(this.offset, size); this.offset -= ofrm; start += ofrm; size -= ofrm; if (size <= 0) { return; } } // ATTACK if (this.attackIt < this.attackTotal) { int atrm = Mathf.Min(this.attackTotal - this.attackIt, size); int end = start + atrm; float totalAt = this.attackTotal; float at = this.attackIt; for (int i = start; i < end; ++i) { data[i] = pfp[i] * (at / totalAt); at += 1.0f; } this.attackIt += atrm; size -= atrm; start += atrm; if (size <= 0) { return; } } // DECAY if (this.decayLeft > 0) { int dcrm = Mathf.Min(this.decayLeft, size); int dcend = start + dcrm; float totalDc = this.decayTotal; float cd = this.decayLeft; float susDiff = 1.0f - this.sustain; for (int i = start; i < dcend; ++i) { data[i] = pfp[i] * (this.sustain + (cd / totalDc) * susDiff); cd -= 1.0f; } this.decayLeft -= dcrm; size -= dcrm; start += dcrm; if (size <= 0) { return; } } // If we're still here, all that's left is sustain for (int i = start; i < start + size; ++i) { data[i] = pfp[i] * this.sustain; } } }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBuffSz, FPCMFactoryGenLimit pcmFactory) { if (this.offset > 0) { int of = Min(this.offset, size); if (this.passOffset == OffsetPass.Silent) { // If silent, we still need to let time pass for it, so // we need to burn it. FPCM fpcm = pcmFactory.GetZeroedFPCM(start, size); float [] a = fpcm.buffer; fixed(float *pa = a) { this.input.Accumulate(pa, start, of, prefBuffSz, pcmFactory); } } else if (this.passOffset == OffsetPass.Pass) { this.input.Accumulate(data, start, of, prefBuffSz, pcmFactory); } else if (this.passOffset == OffsetPass.Hold) { } // Do nothing this.offset -= of; start += of; size -= of; if (size == 0) { return; } } if (this.recordingIt < this.rfs.Length) { int recAmt = Min(size, this.rfs.Length - this.recordingIt); FPCM fpcm = pcmFactory.GetZeroedFPCM(0, size); float [] lbuf = fpcm.buffer; fixed(float *plbuf = lbuf) { this.input.Accumulate(plbuf, 0, recAmt, prefBuffSz, pcmFactory); for (int i = 0; i < recAmt; ++i) { data[start + i] = plbuf[i]; rfs[recordingIt + i] = plbuf[i]; } } this.recordingIt += recAmt; start += recAmt; size -= recAmt; if (this.recordingIt == this.rfs.Length) { // If the buffer size is tiny, we're going to duplicate it to a sane amount // so we don't have many tiny cycles when it comes to replaying it. if (this.rfs.Length < prefBuffSz) { int repCt = this.rfs.Length / prefBuffSz; if (repCt > 1) { float [] rfOld = this.rfs; int oldSz = this.rfs.Length; // Repeat the buffer this.rfs = new float[oldSz * repCt]; for (int i = 1; i < repCt; ++i) { int baseIdx = i * oldSz; for (int j = 0; j < oldSz; ++j) { this.rfs[baseIdx + j] = rfOld[j]; } } } } } } while (size > 0) { this.playbackIt %= this.rfs.Length; int sameCt = Min(this.rfs.Length - this.playbackIt, size); for (int i = 0; i < sameCt; ++i) { data[start + i] = rfs[this.playbackIt + i]; } this.playbackIt += sameCt; start += sameCt; size -= sameCt; } }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBuffSz, FPCMFactoryGenLimit pcmFactory) { if (this.durationSamples <= 0) { if (this.sustain == 0.0f) { // If duration is over and we don't have sustain, early exit. We account // for what we would have written as a timer for Finished() do it doesn't // exit too early. this.durationSamples -= size; } else { // If duration is over, we're in sustain FPCM fsus = pcmFactory.GetZeroedFPCM(start, size); float[] sus = fsus.buffer; fixed(float *psus = sus) { this.gen.Accumulate(psus, start, size, prefBuffSz, pcmFactory); for (int i = start; i < start + size; ++i) { data[i] = psus[i] * this.sustain; } } } return; } // Are we at a time before the decay is activated? // If it's going to be that way for the entire decay, just // relay it. if (this.offsetSamples > size) { this.offsetSamples -= size; this.gen.Accumulate(data, start, size, prefBuffSz, pcmFactory); return; } FPCM fa = pcmFactory.GetZeroedFPCM(start, size); float[] a = fa.buffer; fixed(float *pa = a) { this.gen.Accumulate(pa, start, size, prefBuffSz, pcmFactory); // Are we at a time before the decay is activated, but // will need to start the decay before we exit? if (this.offsetSamples > 0) { int endOS = start + this.offsetSamples; for (int i = start; i < start + endOS; ++i) { data[i] = pa[i]; } size -= this.offsetSamples; start += this.offsetSamples; this.offsetSamples = 0; } // The decay. We have two versions, because if the sustain ramps to zero, we can // avoid some lerp math. float total = totalDurationSamples; int decSamps = Mathf.Min(size, this.durationSamples); int end = start + decSamps; if (this.sustain == 0.0f) { float durs = (float)this.durationSamples; for (int i = start; i < end; ++i) { float lam = durs / total; data[i] = pa[i] * lam; durs -= 1.0f; } durationSamples -= decSamps; } else { float durs = (float)this.durationSamples; for (int i = start; i < end; ++i) { float lam = sustain + durs / total * this.invSustain; data[i] += pa[i] * lam; durs -= 1.0f; } start += decSamps; size -= decSamps; this.durationSamples -= decSamps; // If we finish the ramp in the middle, we need to fill the rest with sustain for (int i = start; i < start + size; ++i) { data[i] = pa[i] * this.sustain; } } } }
unsafe public override void AccumulateImpl(float *data, int start, int size, int prefBufSz, FPCMFactoryGenLimit pcmFactory) { // BYPASS IF NO POINT NOT TO //////////////////////////////////////////////////////////////////////////////// if (this.voices <= 1) { // This will probably rarely ever happen because it defeats the purpose of using // this node, but I'll take the optimization where I can get it. this.input.Accumulate(data, start, size, prefBufSz, pcmFactory); return; } // READ IN NEW INFORMATION BY APPENDING BUFFERS //////////////////////////////////////////////////////////////////////////////// int readAmt = size; while (readAmt > 0) { if (buffers.Count == 0) { FPCM firstEntry = pcmFactory.GetZeroedGlobalFPCM(0, prefBufSz); BufferedFPCM bfpmFirst = new BufferedFPCM(firstEntry, this.nextOffset); this.nextOffset += firstEntry.buffer.Length; this.buffers.Add(bfpmFirst); } int lastIdx = this.buffers.Count - 1; BufferedFPCM last = this.buffers[lastIdx]; int readBufAmt = Min(readAmt, last.readLeft); if (readBufAmt != 0) { int readHead = last.cachedLen - last.readLeft; // Read the new stuff requested float [] wbuf = last.buffer.buffer; fixed(float *pwbuf = wbuf) { this.input.Accumulate( &pwbuf[readHead], 0, readBufAmt, prefBufSz, pcmFactory); } // Update and save it back last.readLeft -= readBufAmt; this.buffers[lastIdx] = last; readAmt -= readBufAmt; } if (readAmt <= 0) { break; } // If it's not enough, create another buffer and continue. We // only create it here, because it will be filled the next // round-about in this loop. // // Exact same as above. FPCM newEntry = pcmFactory.GetZeroedGlobalFPCM(0, prefBufSz); BufferedFPCM bfpm = new BufferedFPCM(newEntry, this.nextOffset); this.nextOffset += newEntry.buffer.Length; this.buffers.Add(bfpm); } // WRITE INTO OUTPUT BUFFER //////////////////////////////////////////////////////////////////////////////// int lowestIndexVal = int.MaxValue; for (int buffIt = 0; buffIt < this.rbuffRef.Length; ++buffIt) { int bsize = size; int bstart = start; BufferRef br = this.rbuffRef[buffIt]; while (bsize > 0) { if (br.totalOffset < 0) { int toSkip = Min(-br.totalOffset, bsize); bsize -= toSkip; bstart += toSkip; br.totalOffset += toSkip; if (bsize <= 0) { break; } } int endOffset = br.totalOffset + bsize; BufferedFPCM buffer = this.buffers[br.idx]; float [] a = buffer.buffer.buffer; // Hmm, tic tac toe int endBuffer = buffer.cachedEnd; // Either going to read to the end of the buffer, or the // end of the requested stream read, whichever comes first int canReadLeft = Min(endOffset, endBuffer) - br.totalOffset; int startingOffset = br.totalOffset - buffer.offset; for (int i = 0; i < canReadLeft; ++i) { data[bstart + i] += br.atten * a[startingOffset + i]; } bstart += canReadLeft; bsize -= canReadLeft; br.totalOffset += canReadLeft; // If there's still more to read, that needs to be done // through the next buffer. if (bsize > 0) { ++br.idx; } } lowestIndexVal = Min(lowestIndexVal, br.idx); this.rbuffRef[buffIt] = br; } // MAINTENENCE, GET RID OF OLD GRODY STUFF //////////////////////////////////////////////////////////////////////////////// if (lowestIndexVal != 0) { for (int i = 0; i < lowestIndexVal; ++i) { this.buffers[i].buffer.Release(); } this.buffers.RemoveRange(0, lowestIndexVal); for (int i = 0; i < this.rbuffRef.Length; ++i) { this.rbuffRef[i].idx -= lowestIndexVal; } } }