machine/rescap.h: Implemented audio potentiometer law. (#13588)

* machine/rescap.h: Implemented audio potentiometer law.
Used it in oberheim/dmx.cpp and linn/linndrum.cpp.

* machine/rescap.h: Function should not be a constexpr.
Also avoiding pow in constexpr constants.
This commit is contained in:
m1macrophage 2025-04-13 12:58:52 -07:00 committed by GitHub
parent 65730f43b4
commit 6794099d89
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 22 additions and 6 deletions

View File

@ -26,6 +26,24 @@ constexpr double RES_VOLTAGE_DIVIDER(double r1, double r2) { return r2 / (r1 + r
#define RES_2_SERIAL(r1,r2) ((r1)+(r2)) #define RES_2_SERIAL(r1,r2) ((r1)+(r2))
// Audio taper (aka "logarithmic") potentiometer law.
// `x` should be in the range 0-1, and so is the return value.
inline double RES_AUDIO_POT_LAW(double x)
{
// The implementation is that of an ideal log potentiometer, based on:
// https://electronics.stackexchange.com/questions/304692/formula-for-logarithmic-audio-taper-pot
// Note that most audio potentiometers are not ideal, but they try to
// approximate this curve.
// The 10% midpoint ("A2" potentiometer curve) is typical for audio
// applications. See any datasheet for a log potentiometer.
constexpr const double MIDPOINT = 0.1;
constexpr const double B = (1.0 / MIDPOINT - 1.0) * (1.0 / MIDPOINT - 1.0); // pow(1.0 / MIDPOINT - 1.0, 2);
constexpr const double A = 1.0 / (B - 1.0);
return A * pow(B, x) - A;
}
// macro for the RC time constant on a 74LS123 with C > 1000pF // macro for the RC time constant on a 74LS123 with C > 1000pF
// R is in ohms, C is in farads // R is in ohms, C is in farads
constexpr double TIME_OF_74LS123(double r, double c) { return 0.45 * r * c; } constexpr double TIME_OF_74LS123(double r, double c) { return 0.45 * r * c; }

View File

@ -65,7 +65,6 @@ Reasons for MACHINE_IMPERFECT_SOUND:
* Missing snare / sidestick volume envelope. * Missing snare / sidestick volume envelope.
* Missing tom / conga LPF and filter envelope. * Missing tom / conga LPF and filter envelope.
* Inaccurate filter for "click". * Inaccurate filter for "click".
* Linear, instead of audio-taper volume sliders and master volume knob.
* Linear, instead of tanh response for hi-hat VCA. * Linear, instead of tanh response for hi-hat VCA.
PCBoards: PCBoards:
@ -856,7 +855,7 @@ void linndrum_audio_device::update_volume_and_pan(int channel)
static constexpr const float C_VOICE = CAP_U(10); static constexpr const float C_VOICE = CAP_U(10);
const s32 volume = m_volume[channel]->read(); const s32 volume = m_volume[channel]->read();
const float r_vol_bottom = volume * R_VOL_MAX / 100.0F; const float r_vol_bottom = R_VOL_MAX * RES_AUDIO_POT_LAW(volume / 100.0F);
const float r_vol_top = R_VOL_MAX - r_vol_bottom; const float r_vol_top = R_VOL_MAX - r_vol_bottom;
const float r_pan_left = m_pan[channel]->read() * R_PAN_MAX / 100.0F; const float r_pan_left = m_pan[channel]->read() * R_PAN_MAX / 100.0F;
const float r_pan_right = R_PAN_MAX - r_pan_left; const float r_pan_right = R_PAN_MAX - r_pan_left;
@ -903,7 +902,7 @@ void linndrum_audio_device::update_master_volume()
{ {
static constexpr const float R_MASTER_VOLUME_MAX = RES_K(10); static constexpr const float R_MASTER_VOLUME_MAX = RES_K(10);
const float r_pot_bottom = m_master_volume->read() * R_MASTER_VOLUME_MAX / 100.0F; const float r_pot_bottom = R_MASTER_VOLUME_MAX * RES_AUDIO_POT_LAW(m_master_volume->read() / 100.0F);
const float r_pot_top = R_MASTER_VOLUME_MAX - r_pot_bottom; const float r_pot_top = R_MASTER_VOLUME_MAX - r_pot_bottom;
const float v_input = RES_VOLTAGE_DIVIDER(r_pot_top, RES_2_PARALLEL(r_pot_bottom, OUTPUT_R_INPUT)); const float v_input = RES_VOLTAGE_DIVIDER(r_pot_top, RES_2_PARALLEL(r_pot_bottom, OUTPUT_R_INPUT));

View File

@ -46,7 +46,6 @@ PCBoards:
Possible audio inaccuracies: Possible audio inaccuracies:
- Some uncertainty on component values for HIHAT and PERC2 (see comments in - Some uncertainty on component values for HIHAT and PERC2 (see comments in
HIHAT_CONFIG and PERC_CONFIG). HIHAT_CONFIG and PERC_CONFIG).
- Linear- instead of audio-taper faders.
- Envelope decay ignores diodes in capacitor discharge path. Given the quick - Envelope decay ignores diodes in capacitor discharge path. Given the quick
decay, and that the error is larger at low volumes, this might not be decay, and that the error is larger at low volumes, this might not be
noticeable. noticeable.
@ -1206,7 +1205,7 @@ void dmx_state::update_mix_level(int voice)
static constexpr const float R_FEEDBACK_RIGHT = RES_K(27); // R29. static constexpr const float R_FEEDBACK_RIGHT = RES_K(27); // R29.
const s32 pot_percent = m_faders[VOICE_TO_FADER_MAP[voice]]->read(); const s32 pot_percent = m_faders[VOICE_TO_FADER_MAP[voice]]->read();
const float r_pot_bottom = P_MAX * pot_percent / 100.0F; const float r_pot_bottom = P_MAX * RES_AUDIO_POT_LAW(pot_percent / 100.0F);
const float r_pot_top = P_MAX - r_pot_bottom; const float r_pot_top = P_MAX - r_pot_bottom;
const float r_mix_left = std::get<0>(MIX_RESISTORS[voice]); const float r_mix_left = std::get<0>(MIX_RESISTORS[voice]);
const float r_mix_right = std::get<1>(MIX_RESISTORS[voice]); const float r_mix_right = std::get<1>(MIX_RESISTORS[voice]);
@ -1237,7 +1236,7 @@ void dmx_state::update_master_volume()
static constexpr const float VOLTAGE_TO_SOUND_SCALER = 0.04F; static constexpr const float VOLTAGE_TO_SOUND_SCALER = 0.04F;
const s32 volume_value = m_master_volume->read(); const s32 volume_value = m_master_volume->read();
const float gain = VOLTAGE_TO_SOUND_SCALER * volume_value / 100.0F; const float gain = VOLTAGE_TO_SOUND_SCALER * RES_AUDIO_POT_LAW(volume_value / 100.0F);
m_left_mixer->set_output_gain(0, gain); m_left_mixer->set_output_gain(0, gain);
m_right_mixer->set_output_gain(0, gain); m_right_mixer->set_output_gain(0, gain);
LOGMASKED(LOG_FADERS, "Master volume changed: %d - %f\n", volume_value, gain); LOGMASKED(LOG_FADERS, "Master volume changed: %d - %f\n", volume_value, gain);