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::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; } } }
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SamplesBuffer *MetronomeTrackNode::readWavFile(const QString &fileName, quint32 &sampleRate) { // Open wave file QFile wavFile(fileName); if (!wavFile.open(QFile::ReadOnly)) { qWarning() << "Failed to open WAV file..."; return nullptr; // Done } // Read in the whole thing QByteArray wavFileContent = wavFile.readAll(); // Define the header components quint8 fileType[4]; quint32 fileSize; quint8 waveName[4]; quint8 fmtName[3]; quint32 fmtLength; quint16 fmtType; quint16 numberOfChannels; quint32 sampleRateXBitsPerSampleXChanngelsDivEight; quint16 bitsPerSampleXChannelsDivEightPointOne; quint16 bitsPerSample; quint8 dataHeader[4]; quint32 dataSize; // Create a data stream to analyze the data QDataStream stream(&wavFileContent, QIODevice::ReadOnly); stream.setByteOrder(QDataStream::LittleEndian); // Now pop off the appropriate data into each header field defined above stream.readRawData((char *)fileType, 4); // "RIFF" stream >> fileSize; // File Size stream.readRawData((char *)waveName, 4); // "WAVE" stream.readRawData((char *)fmtName, 4); // "fmt" stream >> fmtLength; // Format length stream >> fmtType; // Format type stream >> numberOfChannels; // Number of channels stream >> sampleRate; // Sample rate stream >> sampleRateXBitsPerSampleXChanngelsDivEight; // (Sample Rate * BitsPerSample * Channels) / 8 stream >> bitsPerSampleXChannelsDivEightPointOne; // (BitsPerSample * Channels) / 8.1 stream >> bitsPerSample; // Bits per sample stream.readRawData((char *)dataHeader, 4); // "data" header stream >> dataSize; // Data Size int samples = dataSize / numberOfChannels / (bitsPerSample/ 8); SamplesBuffer *buffer = new SamplesBuffer(numberOfChannels, samples); // Now pull out the data qint16 sample = 0; for (int s = 0; s < samples; ++s) { for (int c = 0; c < numberOfChannels; ++c) { stream >> sample; buffer->set(c, s, sample / 32767.0f); } } return buffer; }
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 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); } }
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 MetronomeTrackNode::processReplacing(const SamplesBuffer &in, SamplesBuffer &out, int SampleRate, const Midi::MidiBuffer &midiBuffer) { if (samplesPerBeat <= 0) return; internalInputBuffer.setFrameLenght(out.getFrameLenght()); internalInputBuffer.zero(); SamplesBuffer *samplesBuffer = getBuffer(currentBeat); int samplesToCopy = std::min( (int)(samplesBuffer->getFrameLenght() - beatPosition), out.getFrameLenght()); int nextBeatSample = beatPosition + out.getFrameLenght(); int internalOffset = 0; int clickSoundBufferOffset = beatPosition; if (nextBeatSample > samplesPerBeat) {// next beat starting in this audio buffer? samplesBuffer = getBuffer(currentBeat + 1); internalOffset = samplesPerBeat - beatPosition; samplesToCopy = std::min(nextBeatSample - samplesPerBeat, (long)samplesBuffer->getFrameLenght()); clickSoundBufferOffset = 0; } if (samplesToCopy > 0) internalInputBuffer.set(*samplesBuffer, clickSoundBufferOffset, samplesToCopy, internalOffset); AudioNode::processReplacing(in, out, SampleRate, midiBuffer); }
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::add(const SamplesBuffer &buffer, int internalWriteOffset) { unsigned int framesToProcess = std::min((int)frameLenght, buffer.getFrameLenght()); if (buffer.channels >= channels) { for (unsigned int c = 0; c < channels; ++c) { for (unsigned int s = 0; s < framesToProcess; ++s) samples[c][s + internalWriteOffset] += buffer.samples[c][s]; } } else {// samples is stereo and buffer is mono for (unsigned int s = 0; s < framesToProcess; ++s) { samples[0][s + internalWriteOffset] += buffer.samples[0][s]; samples[1][s + internalWriteOffset] += buffer.samples[0][s]; } } }
void AudioNode::processReplacing(const SamplesBuffer &in, SamplesBuffer &out, int sampleRate, std::vector<midi::MidiMessage> &midiBuffer) { Q_UNUSED(in); if (!isActivated()) return; internalInputBuffer.setFrameLenght(out.getFrameLenght()); internalOutputBuffer.setFrameLenght(out.getFrameLenght()); { QMutexLocker locker(&mutex); for (auto node : connections) { // ask connected nodes to generate audio node->processReplacing(internalInputBuffer, internalOutputBuffer, sampleRate, midiBuffer); } } internalOutputBuffer.set(internalInputBuffer); // if we have no plugins inserted the input samples are just copied to output buffer. static SamplesBuffer tempInputBuffer(2); // process inserted plugins for (int i=0; i < MAX_PROCESSORS_PER_TRACK; ++i) { auto processor = processors[i]; if (processor && !processor->isBypassed()) { tempInputBuffer.setFrameLenght(internalOutputBuffer.getFrameLenght()); tempInputBuffer.set(internalOutputBuffer); // the output from previous plugin is used as input to the next plugin in the chain processor->process(tempInputBuffer, internalOutputBuffer, midiBuffer); // some plugins are blocking the midi messages. If a VSTi can't generate messages the previous messages list will be sended for the next plugin in the chain. The messages list is cleared only when the plugin can generate midi messages. if (processor->isVirtualInstrument() && processor->canGenerateMidiMessages()) midiBuffer.clear(); // only the fresh messages will be passed by the next plugin in the chain auto pulledMessages = pullMidiMessagesGeneratedByPlugins(); midiBuffer.insert(midiBuffer.end(), pulledMessages.begin(), pulledMessages.end()); } } preFaderProcess(internalOutputBuffer); //call overrided preFaderProcess in subclasses to allow some preFader process. internalOutputBuffer.applyGain(gain, leftGain, rightGain, boost); lastPeak.update(internalOutputBuffer.computePeak()); postFaderProcess(internalOutputBuffer); out.add(internalOutputBuffer); }
void SamplesBuffer::add(const SamplesBuffer &buffer, int internalWriteOffset){ //QMutexLocker locker(&mutex); unsigned int framesToProcess = std::min( (int)frameLenght, buffer.getFrameLenght()); // if(framesToProcess < (unsigned int)buffer.getFrameLenght()){ // qWarning() << (buffer.getFrameLenght()-framesToProcess) << " samples discarded"; // } if( buffer.channels >= channels){ for (unsigned int c = 0; c < channels; ++c) { for (unsigned int s = 0; s < framesToProcess; ++s) { samples[c][s + internalWriteOffset] += buffer.samples[c][s]; } } } else{//samples is stereo and buffer is mono for (unsigned int s = 0; s < framesToProcess; ++s) { samples[0][s + internalWriteOffset] += buffer.samples[0][s]; samples[1][s + internalWriteOffset] += buffer.samples[0][s]; } } }