bsnes/nall/dsp/core.hpp

165 lines
3.5 KiB
C++

#pragma once
#include <math.h>
#include <vector>
#include <nall/stdint.hpp>
namespace nall {
struct DSP;
struct Resampler {
Resampler(DSP& dsp) : dsp(dsp) {}
virtual ~Resampler() {}
virtual auto setFrequency() -> void = 0;
virtual auto clear() -> void = 0;
virtual auto sample() -> void = 0;
DSP& dsp;
double frequency = 44100.0;
};
struct DSP {
enum class ResampleEngine : uint {
Nearest,
Linear,
Cosine,
Cubic,
Hermite,
Average,
Sinc,
};
inline DSP();
inline ~DSP();
inline auto setChannels(uint channels) -> void;
inline auto setPrecision(uint precision) -> void;
inline auto setFrequency(double frequency) -> void; //inputFrequency
inline auto setVolume(double volume) -> void;
inline auto setBalance(double balance) -> void;
inline auto setResampler(ResampleEngine resamplingEngine) -> void;
inline auto setResamplerFrequency(double frequency) -> void; //outputFrequency
inline auto sample(int channel[]) -> void;
inline auto pending() const -> bool;
inline auto read(int channel[]) -> void;
inline auto clear() -> void;
protected:
inline auto write(double channel[]) -> void;
inline auto adjustVolume() -> void;
inline auto adjustBalance() -> void;
inline auto clamp(const uint bits, const int input) -> int;
struct Settings {
uint channels;
uint precision;
double frequency;
double volume;
double balance;
//internal
double intensity;
double intensityInverse;
} settings;
Resampler* resampler = nullptr;
#include "buffer.hpp"
Buffer buffer;
Buffer output;
friend class ResampleNearest;
friend class ResampleLinear;
friend class ResampleCosine;
friend class ResampleCubic;
friend class ResampleAverage;
friend class ResampleHermite;
friend class ResampleSinc;
};
#include "resample/nearest.hpp"
#include "resample/linear.hpp"
#include "resample/cosine.hpp"
#include "resample/cubic.hpp"
#include "resample/hermite.hpp"
#include "resample/average.hpp"
#include "resample/sinc.hpp"
#include "settings.hpp"
DSP::DSP() {
setResampler(ResampleEngine::Hermite);
setResamplerFrequency(44100.0);
setChannels(2);
setPrecision(16);
setFrequency(44100.0);
setVolume(1.0);
setBalance(0.0);
clear();
}
DSP::~DSP() {
if(resampler) delete resampler;
}
auto DSP::sample(int channel[]) -> void {
for(auto c : range(settings.channels)) {
buffer.write(c) = (double)channel[c] * settings.intensityInverse;
}
buffer.wroffset++;
resampler->sample();
}
auto DSP::pending() const -> bool {
return output.rdoffset != output.wroffset;
}
auto DSP::read(int channel[]) -> void {
adjustVolume();
adjustBalance();
for(auto c : range(settings.channels)) {
channel[c] = clamp(settings.precision, output.read(c) * settings.intensity);
}
output.rdoffset++;
}
auto DSP::write(double channel[]) -> void {
for(auto c : range(settings.channels)) {
output.write(c) = channel[c];
}
output.wroffset++;
}
auto DSP::adjustVolume() -> void {
for(auto c : range(settings.channels)) {
output.read(c) *= settings.volume;
}
}
auto DSP::adjustBalance() -> void {
if(settings.channels != 2) return; //TODO: support > 2 channels
if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance;
if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance;
}
auto DSP::clamp(const uint bits, const int x) -> int {
const int b = 1U << (bits - 1);
const int m = (1U << (bits - 1)) - 1;
return (x > m) ? m : (x < -b) ? -b : x;
}
auto DSP::clear() -> void {
buffer.clear();
output.clear();
resampler->clear();
}
}