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 SamplesBuffer::set(const SamplesBuffer& buffer, unsigned int bufferOffset, unsigned int samplesToCopy, unsigned int internalOffset){ if(buffer.channels <= 0 || channels <= 0){ return; } unsigned int framesToProcess = std::min(samplesToCopy, buffer.getFrameLenght() - bufferOffset); if(framesToProcess > buffer.frameLenght){//não processa mais samples do que a quantidade existente em buffer framesToProcess = buffer.frameLenght; } if((int)(internalOffset + framesToProcess) > this->getFrameLenght()){ framesToProcess = (internalOffset + framesToProcess) - this->getFrameLenght(); } if(channels == buffer.channels){//channels number are equal for (unsigned int c = 0; c < channels; ++c) { for (unsigned int s = 0; s < framesToProcess; ++s) { samples[c][s + internalOffset] = buffer.samples[c][s + bufferOffset]; } } } else{//different number of channels if(!isMono()){//copy every &buffer samples to LR in this buffer if(!buffer.isMono()){ int channelsToCopy = qMin(channels, buffer.channels); for (int c = 0; c < channelsToCopy; ++c) { for (unsigned int s = 0; s < framesToProcess; ++s) { samples[c][s + internalOffset] = buffer.samples[c][s + bufferOffset]; } } } else{ for (unsigned int s = 0; s < framesToProcess; ++s) { samples[0][s + internalOffset] = buffer.samples[0][s + bufferOffset]; samples[1][s + internalOffset] = buffer.samples[0][s + bufferOffset]; } } } else{//this buffer is mono, but the buffer in parameter is not! Mix down the stereo samples in one mono sample value. for (unsigned int s = 0; s < framesToProcess; ++s) { samples[0][s + internalOffset] = (buffer.samples[0][s + bufferOffset] + buffer.samples[1][s + bufferOffset]) / 2; } } } }
void SamplesBuffer::set(const SamplesBuffer &buffer, uint bufferOffset, uint samplesToCopy, uint internalOffset) { if (buffer.channels == 0 || channels == 0) return; unsigned int framesToProcess = std::min(samplesToCopy, buffer.getFrameLenght() - bufferOffset); if (framesToProcess + bufferOffset > buffer.frameLenght) // fixing bug in some built-in metronome sounds return ; if ((uint)(internalOffset + framesToProcess) > this->getFrameLenght()) framesToProcess = (internalOffset + framesToProcess) - this->getFrameLenght(); const uint bytesToProcess = framesToProcess * sizeof(float); if (!bytesToProcess) return; if (channels == buffer.channels) {// channels number are equal for (unsigned int c = 0; c < channels; ++c) { std::memcpy(&(samples[c][internalOffset]), &(buffer.samples[c][bufferOffset]), bytesToProcess); } } else { // different number of channels if (!isMono()) { // copy every &buffer samples to LR in this buffer if (!buffer.isMono()) { int channelsToCopy = qMin(channels, buffer.channels); for (int c = 0; c < channelsToCopy; ++c) { Q_ASSERT(internalOffset < samples[c].size()); Q_ASSERT(bufferOffset < buffer.samples[c].size()); Q_ASSERT(bufferOffset + framesToProcess < buffer.samples[c].size()); Q_ASSERT(internalOffset + framesToProcess < samples[c].size()); std::memcpy(&(samples[c][internalOffset]), &(buffer.samples[c][bufferOffset]), bytesToProcess); } } else { std::memcpy(&(samples[0][internalOffset]), &(buffer.samples[0][bufferOffset]), bytesToProcess); std::memcpy(&(samples[1][internalOffset]), &(buffer.samples[0][bufferOffset]), bytesToProcess); } } else { // this buffer is mono, but the buffer in parameter is not! Mix down the stereo samples in one mono sample value. for (unsigned int s = 0; s < framesToProcess; ++s) { const int index = s + bufferOffset; float v = (buffer.samples[0][index] + buffer.samples[1][index])/2.0f; samples[0][s + internalOffset] = v; } } } }
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 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; } } }