104 lines
3.1 KiB
C++
104 lines
3.1 KiB
C++
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
namespace trnr {
|
|
class sample {
|
|
public:
|
|
sample(int16_t initial_value = 0) {
|
|
set_value(initial_value);
|
|
instances.push_back(this); // track this instance
|
|
}
|
|
|
|
~sample() {
|
|
// remove this instance from the tracking vector
|
|
auto it = std::find(instances.begin(), instances.end(), this);
|
|
if (it != instances.end()) {
|
|
instances.erase(it);
|
|
}
|
|
}
|
|
|
|
// set value while ensuring the global bit depth
|
|
void set_value(int16_t new_value) {
|
|
int16_t max_val = (1 << (global_bit_depth - 1)) - 1; // Max value for signed bit depth
|
|
int16_t min_val = -(1 << (global_bit_depth - 1)); // Min value for signed bit depth
|
|
|
|
// Clamp the value to the allowed range
|
|
if (new_value > max_val) {
|
|
value = max_val;
|
|
} else if (new_value < min_val) {
|
|
value = min_val;
|
|
} else {
|
|
value = new_value;
|
|
}
|
|
}
|
|
|
|
int16_t get_value() const {
|
|
return value;
|
|
}
|
|
|
|
static void set_global_bit_depth(int depth) {
|
|
if (depth < 1 || depth > 16) {
|
|
throw std::invalid_argument("Bit depth must be between 1 and 16.");
|
|
}
|
|
|
|
// rescale all existing values if the bit depth changes
|
|
float scaling_factor = get_scaling_factor(previous_bit_depth, global_bit_depth);
|
|
|
|
// Rescale all existing instances
|
|
for (auto* instance : instances) {
|
|
instance->value = std::round(instance->value * scaling_factor);
|
|
}
|
|
|
|
previous_bit_depth = global_bit_depth; // Store the old bit depth
|
|
global_bit_depth = depth; // Update the global bit depth
|
|
}
|
|
|
|
static int get_global_bit_depth() {
|
|
return global_bit_depth;
|
|
}
|
|
|
|
// arithmetic operators (all respect global bit depth)
|
|
sample operator+(const sample& other) const {
|
|
return sample(this->value + other.value);
|
|
}
|
|
|
|
sample operator-(const sample& other) const {
|
|
return sample(this->value - other.value);
|
|
}
|
|
|
|
sample operator*(const sample& other) const {
|
|
return sample(this->value * other.value);
|
|
}
|
|
|
|
sample operator/(const sample& other) const {
|
|
if (other.value == 0) {
|
|
throw std::runtime_error("division by zero.");
|
|
}
|
|
return sample(this->value / other.value);
|
|
}
|
|
|
|
// Cast to int16_t for convenience
|
|
operator int16_t() const {
|
|
return value;
|
|
}
|
|
|
|
private:
|
|
int16_t value;
|
|
static int global_bit_depth; // global bit depth
|
|
static int previous_bit_depth; // previous bit depth
|
|
static std::vector<sample*> instances; // track all instances for rescaling
|
|
|
|
// helper function to calculate the scaling factor
|
|
static float get_scaling_factor(int old_bit_depth, int new_bit_depth) {
|
|
if (old_bit_depth == new_bit_depth) {
|
|
return 1.0f; // No scaling needed
|
|
}
|
|
float old_max = (1 << (old_bit_depth - 1)) - 1;
|
|
float new_max = (1 << (new_bit_depth - 1)) - 1;
|
|
return new_max / old_max;
|
|
}
|
|
};
|
|
} |