diff --git a/util/sample.h b/util/sample.h new file mode 100644 index 0000000..3fb1003 --- /dev/null +++ b/util/sample.h @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include + +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 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; + } +}; +} \ No newline at end of file