Added oversampling
This commit is contained in:
parent
d017a63a36
commit
a6a1808403
2 changed files with 68 additions and 18 deletions
|
|
@ -25,6 +25,7 @@
|
||||||
#include "AudioBuffer.h"
|
#include "AudioBuffer.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
|
#include "Oversampler.h"
|
||||||
#include "absl/types/span.h"
|
#include "absl/types/span.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
@ -34,22 +35,67 @@
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
std::unique_ptr<sfz::AudioBuffer<T>> readFromFile(SndfileHandle& sndFile, uint32_t numFrames)
|
std::unique_ptr<sfz::AudioBuffer<T>> upsample2x(const sfz::AudioBuffer<T>& buffer)
|
||||||
{
|
{
|
||||||
auto returnedBuffer = std::make_unique<sfz::AudioBuffer<T>>(sndFile.channels(), numFrames);
|
// auto tempBuffer = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
|
||||||
|
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 2);
|
||||||
|
for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
|
||||||
|
sfz::upsample2xStage(buffer.getConstSpan(channelIdx), outputBuffer->getSpan(channelIdx));
|
||||||
|
}
|
||||||
|
return outputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::unique_ptr<sfz::AudioBuffer<T>> upsample4x(const sfz::AudioBuffer<T>& buffer)
|
||||||
|
{
|
||||||
|
auto tempBuffer = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
|
||||||
|
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 4);
|
||||||
|
for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
|
||||||
|
sfz::upsample2xStage(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer));
|
||||||
|
sfz::upsample4xStage(absl::MakeConstSpan(*tempBuffer), outputBuffer->getSpan(channelIdx));
|
||||||
|
}
|
||||||
|
return outputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::unique_ptr<sfz::AudioBuffer<T>> upsample8x(const sfz::AudioBuffer<T>& buffer)
|
||||||
|
{
|
||||||
|
auto tempBuffer = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 4);
|
||||||
|
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 8);
|
||||||
|
for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
|
||||||
|
sfz::upsample2xStage(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer).first(buffer.getNumFrames() * 2));
|
||||||
|
sfz::upsample4xStage(absl::MakeConstSpan(*tempBuffer).first(buffer.getNumFrames() * 2), absl::MakeSpan(*tempBuffer));
|
||||||
|
sfz::upsample8xStage(absl::MakeConstSpan(*tempBuffer), outputBuffer->getSpan(channelIdx));
|
||||||
|
}
|
||||||
|
return outputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::unique_ptr<sfz::AudioBuffer<T>> readFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor = sfz::Oversampling::x1)
|
||||||
|
{
|
||||||
|
auto baseBuffer = std::make_unique<sfz::AudioBuffer<T>>(sndFile.channels(), numFrames);
|
||||||
if (sndFile.channels() == 1) {
|
if (sndFile.channels() == 1) {
|
||||||
sndFile.readf(returnedBuffer->channelWriter(0), numFrames);
|
sndFile.readf(baseBuffer->channelWriter(0), numFrames);
|
||||||
} else if (sndFile.channels() == 2) {
|
} else if (sndFile.channels() == 2) {
|
||||||
auto tempReadBuffer = std::make_unique<sfz::AudioBuffer<T>>(1, 2 * numFrames);
|
auto tempReadBuffer = std::make_unique<sfz::AudioBuffer<T>>(1, 2 * numFrames);
|
||||||
sndFile.readf(tempReadBuffer->channelWriter(0), numFrames);
|
sndFile.readf(tempReadBuffer->channelWriter(0), numFrames);
|
||||||
sfz::readInterleaved<T>(tempReadBuffer->getSpan(0), returnedBuffer->getSpan(0), returnedBuffer->getSpan(1));
|
auto fileBuffer = std::make_unique<sfz::AudioBuffer<T>>(2, numFrames);
|
||||||
|
sfz::readInterleaved<T>(tempReadBuffer->getSpan(0), baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (factor)
|
||||||
|
{
|
||||||
|
case sfz::Oversampling::x1: return baseBuffer;
|
||||||
|
case sfz::Oversampling::x2: return upsample2x(*baseBuffer);
|
||||||
|
case sfz::Oversampling::x4: return upsample4x(*baseBuffer);
|
||||||
|
case sfz::Oversampling::x8: return upsample8x(*baseBuffer);
|
||||||
|
default: return {};
|
||||||
}
|
}
|
||||||
return returnedBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(const std::string& filename) noexcept
|
absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(const std::string& filename) noexcept
|
||||||
{
|
{
|
||||||
fs::path file{ rootDirectory / filename };
|
fs::path file { rootDirectory / filename };
|
||||||
if (!fs::exists(file))
|
if (!fs::exists(file))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|
@ -76,7 +122,7 @@ absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation
|
||||||
|
|
||||||
bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) noexcept
|
bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) noexcept
|
||||||
{
|
{
|
||||||
fs::path file{ rootDirectory / filename };
|
fs::path file { rootDirectory / filename };
|
||||||
if (!fs::exists(file))
|
if (!fs::exists(file))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -121,10 +167,10 @@ sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) n
|
||||||
void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept
|
void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept
|
||||||
{
|
{
|
||||||
// Update all the preloaded sizes
|
// Update all the preloaded sizes
|
||||||
for (auto& preloadedFile: preloadedFiles) {
|
for (auto& preloadedFile : preloadedFiles) {
|
||||||
const auto numFrames = preloadedFile.second.preloadedData->getNumFrames();
|
const auto numFrames = preloadedFile.second.preloadedData->getNumFrames();
|
||||||
const uint32_t maxOffset = numFrames > this->preloadSize ? numFrames - this->preloadSize : 0;
|
const uint32_t maxOffset = numFrames > this->preloadSize ? numFrames - this->preloadSize : 0;
|
||||||
fs::path file{ rootDirectory / std::string(preloadedFile.first) };
|
fs::path file { rootDirectory / std::string(preloadedFile.first) };
|
||||||
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||||
preloadedFile.second.preloadedData = readFromFile<float>(sndFile, preloadSize + maxOffset);
|
preloadedFile.second.preloadedData = readFromFile<float>(sndFile, preloadSize + maxOffset);
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +183,7 @@ void sfz::FilePool::loadingThread() noexcept
|
||||||
std::this_thread::sleep_for(1ms);
|
std::this_thread::sleep_for(1ms);
|
||||||
for (auto& promise : temporaryFilePromises) {
|
for (auto& promise : temporaryFilePromises) {
|
||||||
if (!promise->dataReady) {
|
if (!promise->dataReady) {
|
||||||
fs::path file{ rootDirectory / std::string(promise->filename) };
|
fs::path file { rootDirectory / std::string(promise->filename) };
|
||||||
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||||
if (sndFile.error() != 0)
|
if (sndFile.error() != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -173,16 +219,24 @@ void sfz::FilePool::cleanupPromises() noexcept
|
||||||
std::iter_swap(promise, sentinel);
|
std::iter_swap(promise, sentinel);
|
||||||
sentinel--;
|
sentinel--;
|
||||||
temporaryFilePromises.pop_back();
|
temporaryFilePromises.pop_back();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
promise++;
|
promise++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept
|
void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept
|
||||||
{
|
{
|
||||||
|
float samplerateChange { static_cast<float>(factor) / static_cast<float>(this->oversamplingFactor) };
|
||||||
|
for (auto& preloadedFile : preloadedFiles) {
|
||||||
|
const auto numFrames = preloadedFile.second.preloadedData->getNumFrames();
|
||||||
|
const uint32_t maxOffset = numFrames > this->preloadSize ? numFrames - this->preloadSize : 0;
|
||||||
|
fs::path file { rootDirectory / std::string(preloadedFile.first) };
|
||||||
|
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||||
|
preloadedFile.second.preloadedData = readFromFile<float>(sndFile, preloadSize + maxOffset);
|
||||||
|
preloadedFile.second.sampleRate *= samplerateChange;
|
||||||
|
}
|
||||||
|
|
||||||
this->oversamplingFactor = factor;
|
this->oversamplingFactor = factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,11 @@
|
||||||
#include "AudioBuffer.h"
|
#include "AudioBuffer.h"
|
||||||
#include "SIMDHelpers.h"
|
#include "SIMDHelpers.h"
|
||||||
#include "ghc/fs_std.hpp"
|
#include "ghc/fs_std.hpp"
|
||||||
#include "moodycamel/readerwriterqueue.h"
|
|
||||||
#include <absl/container/flat_hash_map.h>
|
#include <absl/container/flat_hash_map.h>
|
||||||
#include <mutex>
|
|
||||||
#include <absl/types/optional.h>
|
#include <absl/types/optional.h>
|
||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <sndfile.hh>
|
||||||
|
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
using AudioBufferPtr = std::shared_ptr<AudioBuffer<float>>;
|
using AudioBufferPtr = std::shared_ptr<AudioBuffer<float>>;
|
||||||
|
|
@ -138,7 +137,6 @@ public:
|
||||||
Oversampling getOversamplingFactor() const noexcept;
|
Oversampling getOversamplingFactor() const noexcept;
|
||||||
private:
|
private:
|
||||||
fs::path rootDirectory;
|
fs::path rootDirectory;
|
||||||
|
|
||||||
void loadingThread() noexcept;
|
void loadingThread() noexcept;
|
||||||
|
|
||||||
uint32_t preloadSize { config::preloadSize };
|
uint32_t preloadSize { config::preloadSize };
|
||||||
|
|
@ -148,8 +146,6 @@ private:
|
||||||
bool quitThread { false };
|
bool quitThread { false };
|
||||||
bool emptyQueue { false };
|
bool emptyQueue { false };
|
||||||
|
|
||||||
std::mutex fileHandleMutex;
|
|
||||||
|
|
||||||
std::vector<FilePromisePtr> temporaryFilePromises;
|
std::vector<FilePromisePtr> temporaryFilePromises;
|
||||||
std::vector<FilePromisePtr> promisesToClean;
|
std::vector<FilePromisePtr> promisesToClean;
|
||||||
absl::flat_hash_map<absl::string_view, PreloadedFileHandle> preloadedFiles;
|
absl::flat_hash_map<absl::string_view, PreloadedFileHandle> preloadedFiles;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue