Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ed946aa48 | |||
| e4a489ae48 | |||
| 5baec48c28 | |||
| bec6599f35 | |||
| 2476bab44f | |||
| 023b8192a5 | |||
| 5afc5ff18a | |||
| 9b3e4deb30 | |||
| 4dbf88afcb | |||
| 22eea12b17 | |||
| 3ec3204cd8 | |||
| e5e156ce32 | |||
| 9485c74961 |
52
clip/wavefolder.h
Normal file
52
clip/wavefolder.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace trnr {
|
||||||
|
class wavefolder {
|
||||||
|
public:
|
||||||
|
float amount = 1.f;
|
||||||
|
|
||||||
|
void process_sample(float& _sample)
|
||||||
|
{
|
||||||
|
if (amount > 1.f) { _sample = fold(_sample * amount); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// folds the wave from -1 to 1
|
||||||
|
float fold(float _sample)
|
||||||
|
{
|
||||||
|
while (_sample > 1.0 || _sample < -1.0) {
|
||||||
|
|
||||||
|
if (_sample > 1.0) {
|
||||||
|
_sample = 2.0 - _sample;
|
||||||
|
} else if (_sample < -1.0) {
|
||||||
|
_sample = -2.0 - _sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
// folds the positive part of the wave independently from the negative part.
|
||||||
|
float fold_bipolar(float _sample)
|
||||||
|
{
|
||||||
|
// fold positive values
|
||||||
|
if (_sample > 1.0) {
|
||||||
|
_sample = 2.0 - _sample;
|
||||||
|
|
||||||
|
if (_sample < 0.0) { _sample = -_sample; }
|
||||||
|
|
||||||
|
return fold(_sample);
|
||||||
|
}
|
||||||
|
// fold negative values
|
||||||
|
else if (_sample < -1.0) {
|
||||||
|
_sample = -2.0 - _sample;
|
||||||
|
|
||||||
|
if (_sample > 0.0) { _sample = -_sample; }
|
||||||
|
|
||||||
|
return fold(_sample);
|
||||||
|
} else {
|
||||||
|
return _sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace trnr
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "../clip/wavefolder.h"
|
||||||
#include "tx_envelope.h"
|
#include "tx_envelope.h"
|
||||||
#include "tx_sineosc.h"
|
#include "tx_sineosc.h"
|
||||||
|
|
||||||
@@ -8,27 +9,35 @@ public:
|
|||||||
tx_operator()
|
tx_operator()
|
||||||
: ratio {1}
|
: ratio {1}
|
||||||
, amplitude {1.0f}
|
, amplitude {1.0f}
|
||||||
|
, envelope_enabled {true}
|
||||||
|
, velocity_enabled {true}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
tx_envelope envelope;
|
tx_envelope envelope;
|
||||||
tx_sineosc oscillator;
|
tx_sineosc oscillator;
|
||||||
|
wavefolder folder;
|
||||||
float ratio;
|
float ratio;
|
||||||
float amplitude;
|
float amplitude;
|
||||||
|
bool envelope_enabled;
|
||||||
|
bool velocity_enabled;
|
||||||
|
|
||||||
float process_sample(const bool& gate, const bool& trigger, const float& frequency, const float& velocity,
|
float process_sample(const bool& gate, const bool& trigger, const float& frequency, const float& velocity,
|
||||||
const float& pm = 0)
|
const float& pm = 0)
|
||||||
{
|
{
|
||||||
|
float env = 1.f;
|
||||||
|
|
||||||
float env = envelope.process_sample(gate, trigger);
|
if (envelope_enabled) {
|
||||||
|
env = envelope.process_sample(gate, trigger);
|
||||||
// drifts and sounds better!
|
if (!envelope.is_busy()) return 0.f;
|
||||||
if (envelope.is_busy()) {
|
|
||||||
double osc = oscillator.process_sample(trigger, frequency, pm);
|
|
||||||
return osc * env * velocity;
|
|
||||||
} else {
|
|
||||||
return 0.;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float osc = oscillator.process_sample(trigger, frequency, pm);
|
||||||
|
folder.process_sample(osc);
|
||||||
|
|
||||||
|
float adjusted_velocity = velocity_enabled ? velocity : 1.f;
|
||||||
|
|
||||||
|
return osc * env * adjusted_velocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_samplerate(double samplerate)
|
void set_samplerate(double samplerate)
|
||||||
|
|||||||
155
synth/tx_voice.h
155
synth/tx_voice.h
@@ -8,15 +8,21 @@
|
|||||||
|
|
||||||
namespace trnr {
|
namespace trnr {
|
||||||
|
|
||||||
|
enum mod_dest {
|
||||||
|
mod_dest_out = 0,
|
||||||
|
mod_dest_fm,
|
||||||
|
mod_dest_am
|
||||||
|
};
|
||||||
|
|
||||||
template <typename t_sample>
|
template <typename t_sample>
|
||||||
class tx_voice : public ivoice<t_sample> {
|
class tx_voice : public ivoice<t_sample> {
|
||||||
public:
|
public:
|
||||||
tx_voice()
|
tx_voice()
|
||||||
: algorithm {0}
|
: pitch_env_amt {0.f}
|
||||||
, pitch_env_amt {0.f}
|
|
||||||
, feedback_amt {0.f}
|
, feedback_amt {0.f}
|
||||||
, bit_resolution(12.f)
|
, bit_resolution(12.f)
|
||||||
{
|
{
|
||||||
|
set_glide_time(0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gate = false;
|
bool gate = false;
|
||||||
@@ -25,7 +31,9 @@ public:
|
|||||||
float velocity = 1.f;
|
float velocity = 1.f;
|
||||||
float additional_pitch_mod = 0.f; // modulates pitch in frequency
|
float additional_pitch_mod = 0.f; // modulates pitch in frequency
|
||||||
|
|
||||||
int algorithm;
|
mod_dest op2_dest = mod_dest_fm;
|
||||||
|
mod_dest op3_dest = mod_dest_fm;
|
||||||
|
|
||||||
float pitch_env_amt;
|
float pitch_env_amt;
|
||||||
float feedback_amt;
|
float feedback_amt;
|
||||||
float bit_resolution;
|
float bit_resolution;
|
||||||
@@ -35,6 +43,8 @@ public:
|
|||||||
tx_operator op2;
|
tx_operator op2;
|
||||||
tx_operator op3;
|
tx_operator op3;
|
||||||
|
|
||||||
|
void set_glide_time(float time_ms) { glide = 1 - exp(-1.0 / (time_ms * samplerate / 1000.f)); }
|
||||||
|
|
||||||
void note_on(int _note, float _velocity) override
|
void note_on(int _note, float _velocity) override
|
||||||
{
|
{
|
||||||
this->gate = true;
|
this->gate = true;
|
||||||
@@ -50,40 +60,24 @@ public:
|
|||||||
|
|
||||||
void process_samples(t_sample** _outputs, int _start_index, int _block_size) override
|
void process_samples(t_sample** _outputs, int _start_index, int _block_size) override
|
||||||
{
|
{
|
||||||
float frequency = midi_to_frequency(midi_note + pitch_mod + additional_pitch_mod);
|
target_frequency = midi_to_frequency(midi_note + pitch_mod + additional_pitch_mod);
|
||||||
|
|
||||||
for (int s = _start_index; s < _start_index + _block_size; s++) {
|
for (int s = _start_index; s < _start_index + _block_size; s++) {
|
||||||
|
|
||||||
|
// calculate moving average for portamento
|
||||||
|
current_frequency = (1 - glide) * current_frequency + glide * target_frequency;
|
||||||
|
|
||||||
float pitch_env_signal = pitch_env.process_sample(gate, trigger) * pitch_env_amt;
|
float pitch_env_signal = pitch_env.process_sample(gate, trigger) * pitch_env_amt;
|
||||||
float pitched_freq = frequency + pitch_env_signal;
|
float pitched_freq = current_frequency + pitch_env_signal;
|
||||||
|
|
||||||
float output = 0.f;
|
float signal = process_operators(pitched_freq);
|
||||||
|
|
||||||
// mix operator signals according to selected algorithm
|
|
||||||
switch (algorithm) {
|
|
||||||
case 0:
|
|
||||||
output = calc_algo1(pitched_freq);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
output = calc_algo2(pitched_freq);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
output = calc_algo3(pitched_freq);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
output = calc_algo4(pitched_freq);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
output = calc_algo1(pitched_freq);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset trigger
|
// reset trigger
|
||||||
trigger = false;
|
trigger = false;
|
||||||
|
|
||||||
redux(output, bit_resolution);
|
redux(signal, bit_resolution);
|
||||||
|
|
||||||
_outputs[0][s] += output / 3.;
|
_outputs[0][s] += signal / 3.;
|
||||||
_outputs[1][s] = _outputs[0][s];
|
_outputs[1][s] = _outputs[0][s];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,13 +87,14 @@ public:
|
|||||||
return gate || op1.envelope.is_busy() || op2.envelope.is_busy() || op3.envelope.is_busy();
|
return gate || op1.envelope.is_busy() || op2.envelope.is_busy() || op3.envelope.is_busy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_samplerate(double samplerate) override
|
void set_samplerate(double _samplerate) override
|
||||||
{
|
{
|
||||||
pitch_env.set_samplerate(samplerate);
|
samplerate = _samplerate;
|
||||||
feedback_osc.set_samplerate(samplerate);
|
pitch_env.set_samplerate(_samplerate);
|
||||||
op1.set_samplerate(samplerate);
|
feedback_osc.set_samplerate(_samplerate);
|
||||||
op2.set_samplerate(samplerate);
|
op1.set_samplerate(_samplerate);
|
||||||
op3.set_samplerate(samplerate);
|
op2.set_samplerate(_samplerate);
|
||||||
|
op3.set_samplerate(_samplerate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_phase_reset(bool phase_reset)
|
void set_phase_reset(bool phase_reset)
|
||||||
@@ -119,80 +114,76 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
double samplerate;
|
||||||
const float MOD_INDEX_COEFF = 4.f;
|
const float MOD_INDEX_COEFF = 4.f;
|
||||||
float pitch_mod = 0.f; // modulates pitch in semi-tones
|
float pitch_mod = 0.f; // modulates pitch in semi-tones
|
||||||
|
|
||||||
float calc_algo1(const float frequency)
|
float current_frequency;
|
||||||
|
float target_frequency;
|
||||||
|
float glide;
|
||||||
|
|
||||||
|
float process_op3(const float frequency)
|
||||||
{
|
{
|
||||||
float fb_freq = frequency * op3.ratio;
|
float fb_freq = frequency * op3.ratio;
|
||||||
float fb_mod_index = (feedback_amt * MOD_INDEX_COEFF);
|
float fb_mod_index = feedback_amt * MOD_INDEX_COEFF;
|
||||||
float fb_signal = feedback_osc.process_sample(trigger, fb_freq) * fb_mod_index;
|
float fb_signal = feedback_osc.process_sample(trigger, fb_freq) * fb_mod_index;
|
||||||
|
|
||||||
float op3_Freq = frequency * op3.ratio;
|
float op3_Freq = frequency * op3.ratio;
|
||||||
float op3_mod_index = (op3.amplitude * MOD_INDEX_COEFF);
|
return op3.process_sample(gate, trigger, op3_Freq, velocity, fb_signal) * op3.amplitude;
|
||||||
float op3_signal = op3.process_sample(gate, trigger, op3_Freq, velocity, fb_signal) * op3_mod_index;
|
|
||||||
|
|
||||||
float op2_freq = frequency * op2.ratio;
|
|
||||||
float op2_mod_index = (op2.amplitude * MOD_INDEX_COEFF);
|
|
||||||
float op2_signal = op2.process_sample(gate, trigger, op2_freq, velocity, op3_signal) * op2_mod_index;
|
|
||||||
|
|
||||||
float op1_freq = frequency * op1.ratio;
|
|
||||||
return op1.process_sample(gate, trigger, op1_freq, velocity, op2_signal) * op1.amplitude;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float calc_algo2(const float frequency)
|
float process_op2(const float frequency, const float modulator)
|
||||||
{
|
{
|
||||||
float fb_freq = frequency * op3.ratio;
|
// if patched, op3 modulates the phase of op2
|
||||||
float fb_mod_index = (feedback_amt * MOD_INDEX_COEFF);
|
float pm = op3_dest == mod_dest_fm ? modulator : 0.f;
|
||||||
float fb_signal = feedback_osc.process_sample(trigger, fb_freq) * fb_mod_index;
|
|
||||||
|
|
||||||
float op3_freq = frequency * op3.ratio;
|
float adjusted_freq = frequency * op2.ratio;
|
||||||
float op3_signal = op3.process_sample(gate, trigger, op3_freq, velocity, fb_signal) * op3.amplitude;
|
float signal = op2.process_sample(gate, trigger, adjusted_freq, velocity, pm * MOD_INDEX_COEFF) * op2.amplitude;
|
||||||
|
|
||||||
float op2_freq = frequency * op2.ratio;
|
// if patched, op3 modulated the amplitude of op2
|
||||||
float op2_mod_index = (op2.amplitude * MOD_INDEX_COEFF);
|
if (op3_dest == mod_dest_am) ring_mod(signal, modulator, op3.amplitude);
|
||||||
float op2_signal = op2.process_sample(gate, trigger, op2_freq, velocity) * op2_mod_index;
|
|
||||||
|
|
||||||
float op1_freq = frequency * op1.ratio;
|
return signal;
|
||||||
float op1_signal = op1.process_sample(gate, trigger, op1_freq, velocity, op2_signal) * op1.amplitude;
|
|
||||||
|
|
||||||
return op1_signal + op3_signal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float calc_algo3(const float frequency)
|
float process_op1(const float frequency, const float modulator)
|
||||||
{
|
{
|
||||||
float fb_freq = frequency * op3.ratio;
|
// if patched, op2 modulates the phase of op1
|
||||||
float fb_mod_index = (feedback_amt * MOD_INDEX_COEFF);
|
float pm = op2_dest == mod_dest_fm ? modulator : 0.f;
|
||||||
float fb_signal = feedback_osc.process_sample(trigger, fb_freq) * fb_mod_index;
|
|
||||||
|
|
||||||
float op3_freq = frequency * op3.ratio;
|
|
||||||
float op3_signal = op3.process_sample(gate, trigger, op3_freq, velocity, fb_signal) * op3.amplitude;
|
|
||||||
|
|
||||||
float op2_freq = frequency * op2.ratio;
|
|
||||||
float op2_signal = op2.process_sample(gate, trigger, op2_freq, velocity) * op2.amplitude;
|
|
||||||
|
|
||||||
float op1_freq = frequency * op1.ratio;
|
float op1_freq = frequency * op1.ratio;
|
||||||
float op1_signal = op1.process_sample(gate, trigger, op1_freq, velocity) * op1.amplitude;
|
float signal = op1.process_sample(gate, trigger, op1_freq, velocity, pm * MOD_INDEX_COEFF) * op1.amplitude;
|
||||||
|
|
||||||
return op1_signal + op2_signal + op3_signal;
|
// if patched, op2 modulates the amplitude of op1
|
||||||
|
if (op2_dest == mod_dest_am) ring_mod(signal, modulator, op2.amplitude);
|
||||||
|
|
||||||
|
return signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
float calc_algo4(const float frequency)
|
float process_operators(float frequency)
|
||||||
{
|
{
|
||||||
float fb_freq = frequency * op3.ratio;
|
float op3_signal = process_op3(frequency);
|
||||||
float fb_mod_index = (feedback_amt * MOD_INDEX_COEFF);
|
|
||||||
float fb_signal = feedback_osc.process_sample(trigger, fb_freq) * fb_mod_index;
|
|
||||||
|
|
||||||
float op3_freq = frequency * op3.ratio;
|
float op2_signal = process_op2(frequency, op3_signal);
|
||||||
float op3_mod_index = (op3.amplitude * MOD_INDEX_COEFF);
|
|
||||||
float op3_signal = op3.process_sample(gate, trigger, op3_freq, velocity, fb_signal) * op3_mod_index;
|
|
||||||
|
|
||||||
float op2_freq = frequency * op2.ratio;
|
float op1_signal = process_op1(frequency, op2_signal);
|
||||||
float op2_mod_index = (op2.amplitude * MOD_INDEX_COEFF);
|
|
||||||
float op2_signal = op2.process_sample(gate, trigger, op2_freq, velocity) * op2_mod_index;
|
|
||||||
|
|
||||||
float op1_freq = frequency * op1.ratio;
|
float signal_mix = op1_signal;
|
||||||
return op1.process_sample(gate, trigger, op1_freq, velocity, op2_signal + op3_signal) * op1.amplitude;
|
if (op3_dest == mod_dest_out) { signal_mix += op3_signal; }
|
||||||
|
if (op2_dest == mod_dest_out) { signal_mix += op2_signal; }
|
||||||
|
|
||||||
|
return signal_mix;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_mod(float& carrier, float modulator, float blend)
|
||||||
|
{
|
||||||
|
float dry_lvl = 1.f - blend;
|
||||||
|
float wet_lvl = blend;
|
||||||
|
|
||||||
|
float dry_signal = carrier;
|
||||||
|
float wet_signal = carrier * modulator * 2.0f;
|
||||||
|
|
||||||
|
carrier = dry_lvl * dry_signal + wet_lvl * wet_signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
float redux(float& value, float resolution)
|
float redux(float& value, float resolution)
|
||||||
|
|||||||
33
util/averager.h
Normal file
33
util/averager.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace trnr {
|
||||||
|
template <typename t_sample, unsigned int sample_size>
|
||||||
|
class averager {
|
||||||
|
public:
|
||||||
|
averager() { samples.fill(0); }
|
||||||
|
|
||||||
|
t_sample process_sample(t_sample& _sample)
|
||||||
|
{
|
||||||
|
t_sample sum = t_sample(0);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < sample_size; i++) {
|
||||||
|
|
||||||
|
if (i < sample_size - 1) {
|
||||||
|
// shift to the left
|
||||||
|
samples[i] = samples[i + 1];
|
||||||
|
} else {
|
||||||
|
// put new sample last
|
||||||
|
samples[i] = _sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += samples[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum / sample_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<t_sample, sample_size> samples;
|
||||||
|
};
|
||||||
|
} // namespace trnr
|
||||||
104
util/sample.h
Normal file
104
util/sample.h
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user