diff --git a/sources/FilePool.cpp b/sources/FilePool.cpp index c66e7fc8..f46013f4 100644 --- a/sources/FilePool.cpp +++ b/sources/FilePool.cpp @@ -1,6 +1,11 @@ #include "FilePool.h" +#include "Globals.h" +#include "absl/types/span.h" #include +#include #include +#include +#include using namespace std::chrono_literals; std::optional sfz::FilePool::getFileInformation(std::string_view filename) @@ -10,21 +15,47 @@ std::optional sfz::FilePool::getFileInformation( return {}; SndfileHandle sndFile(reinterpret_cast(file.c_str())); + if (sndFile.channels() != 1 && sndFile.channels() != 2) { + DBG("Missing logic for " << sndFile.channels() <<", discarding sample " << filename); + return {}; + } FileInformation returnedValue; returnedValue.end = static_cast(sndFile.frames()); returnedValue.sampleRate = static_cast(sndFile.samplerate()); SF_INSTRUMENT instrumentInfo; sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo)); - if (instrumentInfo.loop_count == 1) { returnedValue.loopBegin = instrumentInfo.loops[0].start; returnedValue.loopEnd = instrumentInfo.loops[0].end; } - auto preloadedSize = std::min(returnedValue.end, static_cast(config::preloadSize)); + + const auto preloadedSize = [&]() { + if (config::preloadSize == 0) + return returnedValue.end; + else + return std::min(returnedValue.end, static_cast(config::preloadSize)); + }(); returnedValue.preloadedData = std::make_shared>(preloadedSize); - sndFile.readf(tempReadBuffer.data(), preloadedSize); - returnedValue.preloadedData->readInterleaved(absl::MakeSpan(tempReadBuffer).first(preloadedSize)); preloadedData[filename] = returnedValue.preloadedData; + // auto tempReadBuffer = std::make_unique>(2 * preloadedSize); + if (sndFile.channels() == 1) { + tempReadBuffer.resize(preloadedSize); + sndFile.readf(tempReadBuffer.data(), preloadedSize); + std::copy(tempReadBuffer.begin(), tempReadBuffer.end(), returnedValue.preloadedData->begin(Channel::left)); + std::copy(tempReadBuffer.begin(), tempReadBuffer.end(), returnedValue.preloadedData->begin(Channel::right)); + } else if (sndFile.channels() == 2) { + tempReadBuffer.resize(preloadedSize * 2); + sndFile.readf(tempReadBuffer.data(), preloadedSize); + returnedValue.preloadedData->readInterleaved(tempReadBuffer); + } + // std::stringstream dumpName { }; + // dumpName << filename << ".preloaded" << preloadedFilesWritten << ".bin"; + // dumpName << filename << ".preloaded" << ".bin"; + // std::ofstream ofile(dumpName.str(), std::ios::binary); + // preloadedFilesWritten++; + // for (auto& floatValue: tempReadBuffer) + // ofile.write((char*) &floatValue, sizeof(float)); + // ofile.close(); // char buffer [2048] ; // sndFile.command(SFC_GET_LOG_INFO, buffer, sizeof(buffer)) ; // DBG(buffer); @@ -58,13 +89,19 @@ void sfz::FilePool::loadingThread() } SndfileHandle sndFile(reinterpret_cast(file.c_str())); - // auto deleteAndTrackBuffers = [this] - std::unique_ptr, std::function*)>> fileLoaded(new StereoBuffer(fileToLoad.numFrames), deleteAndTrackBuffers); - auto readBuffer = std::make_unique>(fileToLoad.numFrames * 2); - sndFile.readf(readBuffer->data(), fileToLoad.numFrames); - fileLoaded->readInterleaved(*readBuffer); - ASSERT(fileLoaded != nullptr); - fileBuffers++; + auto fileLoaded = std::make_unique>(fileToLoad.numFrames); + if (sndFile.channels() == 1) { + tempReadBuffer.resize(fileToLoad.numFrames); + sndFile.readf(tempReadBuffer.data(), fileToLoad.numFrames); + std::copy(tempReadBuffer.begin(), tempReadBuffer.end(), fileLoaded->begin(Channel::left)); + std::copy(tempReadBuffer.begin(), tempReadBuffer.end(), fileLoaded->begin(Channel::right)); + } else if (sndFile.channels() == 2) { + tempReadBuffer.resize(fileToLoad.numFrames * 2); + sndFile.readf(tempReadBuffer.data(), fileToLoad.numFrames); + fileLoaded->readInterleaved(tempReadBuffer); + } + // std::unique_ptr, std::function*)>> fileLoaded(new StereoBuffer(framesRead), deleteAndTrackBuffers); + // fileBuffers++; fileToLoad.voice->setFileData(std::move(fileLoaded)); } } \ No newline at end of file diff --git a/sources/FilePool.h b/sources/FilePool.h index 93aa9e1d..d106874b 100644 --- a/sources/FilePool.h +++ b/sources/FilePool.h @@ -55,14 +55,12 @@ private: }; inline static std::atomic fileBuffers { 0 }; - + int preloadedFilesWritten {0}; moodycamel::BlockingReaderWriterQueue loadingQueue; void loadingThread(); std::thread fileLoadingThread; bool quitThread { false }; - Buffer tempReadBuffer { config::preloadSize * 2 }; - - // std::map>> preloadedData; + Buffer tempReadBuffer; absl::flat_hash_map>> preloadedData; LEAK_DETECTOR(FilePool); }; diff --git a/sources/Globals.h b/sources/Globals.h index 8d44c3d4..c0cfd887 100644 --- a/sources/Globals.h +++ b/sources/Globals.h @@ -27,6 +27,7 @@ constexpr bool fill { true }; constexpr bool gain { false }; constexpr bool mathfuns { false }; constexpr bool loopingSFZIndex { true }; +constexpr bool saturatingSFZIndex { true }; constexpr bool linearRamp { false }; constexpr bool multiplicativeRamp { true }; constexpr bool add { false }; diff --git a/sources/Main.cpp b/sources/Main.cpp index 5ca3fa42..09157218 100644 --- a/sources/Main.cpp +++ b/sources/Main.cpp @@ -92,7 +92,7 @@ int process(jack_nframes_t numFrames, void* arg [[maybe_unused]]) break; } } - + // DBG("Num frames: " << numFrames); StereoSpan output { jack_port_get_buffer(outputPort1, numFrames), jack_port_get_buffer(outputPort2, numFrames), @@ -100,6 +100,7 @@ int process(jack_nframes_t numFrames, void* arg [[maybe_unused]]) }; synth->renderBlock(output); + return 0; } diff --git a/sources/Region.cpp b/sources/Region.cpp index 09ae77a0..58615c35 100644 --- a/sources/Region.cpp +++ b/sources/Region.cpp @@ -323,6 +323,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) break; case hash("pitch_random"): setValueFromOpcode(opcode, pitchRandom, Default::pitchRandomRange); + pitchDistribution.param(std::uniform_int_distribution::param_type(-pitchRandom, pitchRandom)); break; case hash("transpose"): setValueFromOpcode(opcode, transpose, Default::transposeRange); diff --git a/sources/Region.h b/sources/Region.h index 5654e198..043f14a7 100644 --- a/sources/Region.h +++ b/sources/Region.h @@ -32,6 +32,16 @@ struct Region { void registerAftertouch(int channel, uint8_t aftertouch); void registerTempo(float secondsPerQuarter); bool isStereo() const noexcept; + float getBasePitchVariation(int noteNumber, uint8_t velocity) noexcept + { + auto pitchVariationInCents = pitchKeytrack * (noteNumber - (int)pitchKeycenter); // note difference with pitch center + pitchVariationInCents += tune; // sample tuning + pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose + pitchVariationInCents += velocity / 127 * pitchVeltrack; // track velocity + if (pitchRandom > 0) + pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes + return centsFactor(pitchVariationInCents); + } float getBaseGain() { float baseGaindB { volume }; @@ -51,6 +61,13 @@ struct Region { { return min(sampleEnd, loopRange.getEnd()); } + bool canUsePreloadedData() + { + if (preloadedData == nullptr) + return false; + + return trueSampleEnd() < static_cast(preloadedData->getNumFrames()); + } bool parseOpcode(const Opcode& opcode); // Sound source: sample playback @@ -152,6 +169,7 @@ private: std::uniform_real_distribution gainDistribution { -sfz::Default::ampRandom, sfz::Default::ampRandom }; std::uniform_real_distribution delayDistribution { 0, sfz::Default::delayRandom }; std::uniform_int_distribution offsetDistribution { 0, sfz::Default::offsetRandom }; + std::uniform_int_distribution pitchDistribution { -sfz::Default::pitchRandom, sfz::Default::pitchRandom }; LEAK_DETECTOR(Region); }; diff --git a/sources/SIMDDummy.cpp b/sources/SIMDDummy.cpp index 815f9137..9bd13329 100644 --- a/sources/SIMDDummy.cpp +++ b/sources/SIMDDummy.cpp @@ -56,11 +56,18 @@ void applyGain(absl::Span gain, absl::Span -void loopingSFZIndex(absl::Span jumps, absl::Span leftCoeff, absl::Span rightCoeff, absl::Span indices, float floatIndex, float loopEnd, float loopStart) noexcept +float loopingSFZIndex(absl::Span jumps, absl::Span leftCoeff, absl::Span rightCoeff, absl::Span indices, float floatIndex, float loopEnd, float loopStart) noexcept { - loopingSFZIndex(jumps, leftCoeff, rightCoeff, indices, floatIndex, loopEnd, loopStart); + return loopingSFZIndex(jumps, leftCoeff, rightCoeff, indices, floatIndex, loopEnd, loopStart); } +template <> +float saturatingSFZIndex(absl::Span jumps, absl::Span leftCoeff, absl::Span rightCoeff, absl::Span indices, float floatIndex, float loopEnd) noexcept +{ + return saturatingSFZIndex(jumps, leftCoeff, rightCoeff, indices, floatIndex, loopEnd); +} + + template <> float linearRamp(absl::Span output, float start, float step) noexcept { diff --git a/sources/SIMDHelpers.h b/sources/SIMDHelpers.h index 9792d13f..7bb34d09 100644 --- a/sources/SIMDHelpers.h +++ b/sources/SIMDHelpers.h @@ -133,7 +133,7 @@ inline void snippetSaturatingIndex(const T*& jump, T*& leftCoeff, T*& rightCoeff } template -void saturatingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs, absl::Span indices, T floatIndex, T loopEnd) noexcept +float saturatingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs, absl::Span indices, T floatIndex, T loopEnd) noexcept { ASSERT(indices.size() >= jumps.size()); ASSERT(indices.size() == leftCoeffs.size()); @@ -148,10 +148,11 @@ void saturatingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, abs while (jump < sentinel) snippetSaturatingIndex(jump, leftCoeff, rightCoeff, index, floatIndex, loopEnd); + return floatIndex; } template <> -void saturatingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs, absl::Span indices, float floatIndex, float loopEnd) noexcept; +float saturatingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs, absl::Span indices, float floatIndex, float loopEnd) noexcept; template inline void snippetLoopingIndex(const T*& jump, T*& leftCoeff, T*& rightCoeff, int*& index, T& floatIndex, T loopEnd, T loopStart) @@ -169,7 +170,7 @@ inline void snippetLoopingIndex(const T*& jump, T*& leftCoeff, T*& rightCoeff, i } template -void loopingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs, absl::Span indices, T floatIndex, T loopEnd, T loopStart) noexcept +float loopingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs, absl::Span indices, T floatIndex, T loopEnd, T loopStart) noexcept { ASSERT(indices.size() >= jumps.size()); ASSERT(indices.size() == leftCoeffs.size()); @@ -184,10 +185,11 @@ void loopingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl:: while (jump < sentinel) snippetLoopingIndex(jump, leftCoeff, rightCoeff, index, floatIndex, loopEnd, loopStart); + return floatIndex; } template <> -void loopingSFZIndex(absl::Span jumps, absl::Span leftCoeff, absl::Span rightCoeff, absl::Span indices, float floatIndex, float loopEnd, float loopStart) noexcept; +float loopingSFZIndex(absl::Span jumps, absl::Span leftCoeff, absl::Span rightCoeff, absl::Span indices, float floatIndex, float loopEnd, float loopStart) noexcept; template inline void snippetGain(T gain, const T*& input, T*& output) diff --git a/sources/SIMDSSE.cpp b/sources/SIMDSSE.cpp index 98591445..b9e55bdc 100644 --- a/sources/SIMDSSE.cpp +++ b/sources/SIMDSSE.cpp @@ -283,7 +283,7 @@ void applyGain(absl::Span gain, absl::Span -void loopingSFZIndex(absl::Span jumps, +float loopingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs, absl::Span indices, @@ -341,10 +341,11 @@ void loopingSFZIndex(absl::Span jumps, floatIndex = _mm_cvtss_f32(mmFloatIndex); while (jump < sentinel) snippetLoopingIndex(jump, leftCoeff, rightCoeff, index, floatIndex, loopEnd, loopStart); + return floatIndex; } template <> -void saturatingSFZIndex(absl::Span jumps, +float saturatingSFZIndex(absl::Span jumps, absl::Span leftCoeffs, absl::Span rightCoeffs, absl::Span indices, @@ -398,6 +399,7 @@ void saturatingSFZIndex(absl::Span jumps, floatIndex = _mm_cvtss_f32(mmFloatIndex); while (jump < sentinel) snippetSaturatingIndex(jump, leftCoeff, rightCoeff, index, floatIndex, loopEnd); + return floatIndex; } template <> diff --git a/sources/StereoSpan.h b/sources/StereoSpan.h index 561c4320..b3249542 100644 --- a/sources/StereoSpan.h +++ b/sources/StereoSpan.h @@ -34,7 +34,14 @@ public: ASSERT(leftBuffer.size() == rightBuffer.size()); } - StereoSpan(StereoBuffer& buffer) + StereoSpan(StereoBuffer&& buffer) + : numFrames(buffer.getNumFrames()) + , leftBuffer(buffer.getSpan(Channel::left)) + , rightBuffer(buffer.getSpan(Channel::right)) + { + } + + StereoSpan(StereoBuffer&& buffer) : leftBuffer(buffer.getConstSpan(Channel::left)) , rightBuffer(buffer.getConstSpan(Channel::right)) , numFrames(buffer.getNumFrames()) @@ -48,6 +55,13 @@ public: { } + StereoSpan(StereoBuffer& buffer) + : leftBuffer(buffer.getConstSpan(Channel::left)) + , rightBuffer(buffer.getConstSpan(Channel::right)) + , numFrames(buffer.getNumFrames()) + { + } + StereoSpan(StereoBuffer& buffer, size_t numFrames) : numFrames(numFrames) , leftBuffer(buffer.getSpan(Channel::left).first(numFrames)) @@ -74,6 +88,7 @@ public: , leftBuffer(span.left()) , rightBuffer(span.right()) { + } void fill(Type value) noexcept diff --git a/sources/Synth.h b/sources/Synth.h index 46cdd8d0..3ad41821 100644 --- a/sources/Synth.h +++ b/sources/Synth.h @@ -62,11 +62,11 @@ public: { ScopedFTZ ftz; buffer.fill(0.0f); - StereoSpan tempSpan { tempBuffer, buffer.size() }; for (auto& voice : voices) { voice->renderBlock(tempSpan); buffer.add(tempSpan); + tempBuffer.fill(0.0f); } } @@ -108,6 +108,7 @@ public: } } } + void cc(int delay, int channel, int ccNumber, uint8_t ccValue) { for (auto& voice : voices) @@ -155,7 +156,7 @@ private: activeVoices++; } DBG("Active voices:" << activeVoices << " | Stray buffers: " << FilePool::getFileBuffers()); - std::this_thread::sleep_for(1s); + std::this_thread::sleep_for(200ms); } } diff --git a/sources/Voice.h b/sources/Voice.h index 07e8d885..66af1ac7 100644 --- a/sources/Voice.h +++ b/sources/Voice.h @@ -41,10 +41,11 @@ public: state = State::playing; speedRatio = static_cast(region->sampleRate / this->sampleRate); + pitchRatio = region->getBasePitchVariation(number, value); baseGain = region->getBaseGain(); sourcePosition = region->getOffset(); initialDelay = delay + region->getDelay(); - baseFrequency = midiNoteFrequency(number); + baseFrequency = midiNoteFrequency(number) * pitchRatio; prepareEGEnvelope(delay, value); } @@ -64,10 +65,10 @@ public: normalizePercents(region->amplitudeEG.getStart(ccState, velocity))); } - void setFileData(std::unique_ptr, std::function*)>> file) + void setFileData(std::unique_ptr> file) { fileData = std::move(file); - dataReady.store(true, std::memory_order_seq_cst); + dataReady.store(true); } bool isFree() @@ -114,8 +115,10 @@ public: this->samplesPerBlock = samplesPerBlock; tempBuffer1.resize(samplesPerBlock); tempBuffer2.resize(samplesPerBlock); + indexBuffer.resize(samplesPerBlock); tempSpan1 = absl::MakeSpan(tempBuffer1); tempSpan2 = absl::MakeSpan(tempBuffer2); + indexSpan = absl::MakeSpan(indexBuffer); } void renderBlock(StereoSpan buffer) @@ -129,13 +132,14 @@ public: if (region->isGenerator()) fillWithGenerator(buffer); - else + else fillWithData(buffer); - egEnvelope.getBlock(tempSpan1.first(numSamples)); - buffer.applyGain(tempSpan1); + // buffer.applyGain(baseGain); - buffer.applyGain(baseGain); + auto envelopeSpan = tempSpan1.first(numSamples); + egEnvelope.getBlock(envelopeSpan); + // buffer.applyGain(envelopeSpan); if (!egEnvelope.isSmoothing()) reset(); @@ -143,20 +147,57 @@ public: void fillWithData(StereoSpan buffer) { - const StereoSpan source([&]() -> StereoBuffer& { - // TODO: shouldn't need to check fileData here, something is a bit strange... - if (dataReady.load(std::memory_order_seq_cst) && fileData != nullptr) - return *fileData; + auto source { [&]() { + if (region->canUsePreloadedData() || !dataReady) + return StereoSpan(*region->preloadedData); else - return *region->preloadedData; - }()); + return StereoSpan(*fileData); + }()}; - const auto actualBlockSize = min(buffer.size(), source.size() - sourcePosition); - buffer.add(source.subspan(sourcePosition, actualBlockSize)); - sourcePosition += actualBlockSize; + auto indices = indexSpan.first(buffer.size()); + auto jumps = tempSpan1.first(buffer.size()); + auto leftCoeffs = tempSpan1.first(buffer.size()); + auto rightCoeffs = tempSpan2.first(buffer.size()); - if (sourcePosition == source.size()) + ::fill(jumps, pitchRatio * speedRatio); + if (region->shouldLoop() && region->trueSampleEnd() <= source.size()) { + floatPosition = ::loopingSFZIndex( + jumps, + leftCoeffs, + rightCoeffs, + indices, + floatPosition, + region->trueSampleEnd() - 1, + region->loopRange.getStart()); + } else { + floatPosition = ::saturatingSFZIndex( + jumps, + leftCoeffs, + rightCoeffs, + indices, + floatPosition, + source.size() - 1); + } + + auto ind = indices.data(); + auto leftCoeff = leftCoeffs.data(); + auto rightCoeff = rightCoeffs.data(); + auto left = buffer.left().data(); + auto right = buffer.right().data(); + while (ind < indices.end()) { + *left = source.left()[*ind] * (*leftCoeff) + source.left()[*ind + 1] * (*rightCoeff); + *right = source.right()[*ind] * (*leftCoeff) + source.right()[*ind + 1] * (*rightCoeff); + left++; + right++; + ind++; + leftCoeff++; + rightCoeff++; + } + + if (!region->shouldLoop() && (floatPosition + 1.01) > source.size()) { + DBG("Releasing " << region->sample); egEnvelope.startRelease(buffer.size()); + } } void fillWithGenerator(StereoSpan buffer) @@ -168,7 +209,7 @@ public: phase = ::linearRamp(tempSpan1, phase, step); ::sin(tempSpan1.first(buffer.size()), buffer.left()); absl::c_copy(buffer.left(), buffer.right().begin()); - + sourcePosition += buffer.size(); } @@ -209,6 +250,8 @@ public: if (region != nullptr) { DBG("Reset voice with sample " << region->sample); } + sourcePosition = 0; + floatPosition = 0.0f; region = nullptr; noteIsOff = false; } @@ -242,15 +285,19 @@ private: float phase { 0.0f }; uint32_t sourcePosition { 0 }; + float floatPosition { 0.0f }; uint32_t initialDelay { 0 }; std::atomic dataReady { false }; - std::unique_ptr, std::function*)>> fileData { nullptr }; + // std::unique_ptr, std::function*)>> fileData { nullptr }; + std::unique_ptr> fileData { nullptr }; Buffer tempBuffer1; Buffer tempBuffer2; + Buffer indexBuffer; absl::Span tempSpan1 { absl::MakeSpan(tempBuffer1) }; absl::Span tempSpan2 { absl::MakeSpan(tempBuffer2) }; + absl::Span indexSpan { absl::MakeSpan(indexBuffer) }; int samplesPerBlock { config::defaultSamplesPerBlock }; double sampleRate { config::defaultSampleRate };