void LooperLayer::setSamples(const SamplesBuffer &samples) { zero(); uint samplesToCopy = qMin(samples.getFrameLenght(), lastCycleLenght); if (!samplesToCopy) { return; } uint bytesToCopy = samplesToCopy * sizeof(float); Q_ASSERT(leftChannel.capacity() >= samplesToCopy); Q_ASSERT(rightChannel.capacity() >= samplesToCopy); std::memcpy(&(leftChannel[0]), samples.getSamplesArray(0), bytesToCopy); if (samples.isMono()) std::memcpy(&(rightChannel[0]), samples.getSamplesArray(0), bytesToCopy); else std::memcpy(&(rightChannel[0]), samples.getSamplesArray(1), bytesToCopy); availableSamples = samplesToCopy; // rebuild peaks cache lastCacheComputationSample = 0; peaksCache.clear(); if (lastSamplesPerPeak) { while (availableSamples - lastCacheComputationSample >= lastSamplesPerPeak) { // enough samples to cache a new max peak? peaksCache.push_back(computeMaxPeak(lastCacheComputationSample, lastSamplesPerPeak)); lastCacheComputationSample += lastSamplesPerPeak; } } }
void LooperLayer::append(const SamplesBuffer &samples, uint samplesToAppend, uint startPosition) { int toAppend = qMin(static_cast<uint>(leftChannel.capacity() - startPosition), samplesToAppend); if (!toAppend) { qCritical() << "toAppend:" << toAppend; return; } const uint sizeInBytes = toAppend * sizeof(float); const int secondChannelIndex = samples.isMono() ? 0 : 1; std::memcpy(&(leftChannel[0]) + startPosition, samples.getSamplesArray(0), sizeInBytes); std::memcpy(&(rightChannel[0]) + startPosition, samples.getSamplesArray(secondChannelIndex), sizeInBytes); availableSamples += toAppend; //Q_ASSERT(availableSamples <= leftChannel.capacity()); // build peaks cache if (lastSamplesPerPeak) { if (lastCacheComputationSample < startPosition - lastSamplesPerPeak) lastCacheComputationSample = startPosition; while ((startPosition + samplesToAppend) - lastCacheComputationSample >= lastSamplesPerPeak && lastCacheComputationSample < leftChannel.size()) { // enough samples to cache a new max peak? peaksCache.push_back(computeMaxPeak(lastCacheComputationSample, lastSamplesPerPeak)); lastCacheComputationSample += lastSamplesPerPeak; } } }
void LooperLayer::mixTo(SamplesBuffer &outBuffer, uint samplesToMix, uint intervalPosition, float looperMainGain) { bool canMix = samplesToMix > 0 && (muteState == LooperLayer::Unmuted || muteState == LooperLayer::WaitingToMute); if (canMix) { float *internalChannels[] = {&(leftChannel[0]), &(rightChannel[0])}; const uint secondChannelIndex = (outBuffer.isMono()) ? 0 : 1; float *bufferChannels[] = {outBuffer.getSamplesArray(0), outBuffer.getSamplesArray(secondChannelIndex)}; uint channels = outBuffer.getChannels(); const float mainGain = looperMainGain * gain; const float finalLeftGain = mainGain * leftGain; const float finalRightGain = mainGain * rightGain; float gains[] = {finalLeftGain, finalRightGain}; for (uint c = 0; c < channels; ++c) { for (uint s = 0; s < samplesToMix; ++s) { const uint offset = s + intervalPosition; bufferChannels[c][s] += internalChannels[c][offset] * gains[c]; } } } }
void SamplesBuffer::set(const SamplesBuffer &buffer, int bufferChannelOffset, int channelsToCopy) { if (buffer.channels <= 0 || channels <= 0) return; int framesToCopy = std::min(buffer.getFrameLenght(), (int)frameLenght); int channelsToProcess = std::min(channelsToCopy, std::min(buffer.getChannels(), (int)channels)); if (channelsToProcess + bufferChannelOffset <= buffer.getChannels()) {// avoid invalid channel index int bytesToCopy = framesToCopy * sizeof(float); for (int c = 0; c < channelsToProcess; ++c) memcpy((void *)getSamplesArray(c), buffer.getSamplesArray( c + bufferChannelOffset), bytesToCopy); } }
SamplesBuffer *createResampledBuffer(const SamplesBuffer &buffer, int originalSampleRate, int finalSampleRate) { int finalSize = (double)finalSampleRate/originalSampleRate * buffer.getFrameLenght(); int channels = buffer.getChannels(); SamplesBuffer *newBuffer = new SamplesBuffer(channels, finalSize); for (int c = 0; c < channels; ++c) { ResamplerTest resampler; resampler.process(buffer.getSamplesArray(c), buffer.getFrameLenght(), newBuffer->getSamplesArray(c), finalSize); } return newBuffer; }
void LooperLayer::overdub(const SamplesBuffer &samples, uint samplesToMix, uint startPosition) { if (!samples.isMono()) { float *internalChannels[] = {&(leftChannel[startPosition]), &(rightChannel[startPosition])}; float *samplesArray[] = {samples.getSamplesArray(0), samples.getSamplesArray(1)}; for (uint s = 0; s < samplesToMix; ++s) { internalChannels[0][s] += samplesArray[0][s]; // left channel internalChannels[1][s] += samplesArray[1][s]; // right channel } } else { float *internalChannels[] = {&(leftChannel[startPosition]), &(rightChannel[startPosition])}; float *samplesArray = samples.getSamplesArray(0); for (uint s = 0; s < samplesToMix; ++s) { internalChannels[0][s] += samplesArray[s]; } } if (availableSamples < startPosition + samplesToMix) availableSamples = startPosition + samplesToMix; // build peaks cache when overdubbing if (lastSamplesPerPeak) { const uint position = startPosition + samplesToMix; while (position - lastCacheComputationSample >= lastSamplesPerPeak) { // enough samples to cache a new max peak? const int peakIndex = (position/lastSamplesPerPeak) - 1; float lastPeak = computeMaxPeak(lastCacheComputationSample, lastSamplesPerPeak); if (peakIndex >= 0 && static_cast<uint>(peakIndex) < peaksCache.size()) peaksCache[peakIndex] = lastPeak; else peaksCache.push_back(lastPeak); lastCacheComputationSample += lastSamplesPerPeak; } } }
void SamplesBuffer::set(const SamplesBuffer &buffer, int bufferChannelOffset, int channelsToCopy) { if (buffer.channels == 0 || channels == 0) return; int framesToCopy = std::min(buffer.getFrameLenght(), frameLenght); int channelsToProcess = std::min(channelsToCopy, std::min(buffer.getChannels(), static_cast<int>(channels))); if (channelsToProcess + bufferChannelOffset > buffer.getChannels()) return; // avoid crash acessing invalid memory int bytesToCopy = framesToCopy * sizeof(float); for (int c = 0; c < channelsToProcess; ++c) { memcpy((void *)getSamplesArray(c), buffer.getSamplesArray(c + bufferChannelOffset), bytesToCopy); } }