mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-29 12:50:28 +00:00
175 lines
6.1 KiB
C++
175 lines
6.1 KiB
C++
//
|
|
// Operator.hpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 15/04/2020.
|
|
// Copyright © 2020 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#ifndef Operator_hpp
|
|
#define Operator_hpp
|
|
|
|
#include <cstdint>
|
|
#include "Tables.hpp"
|
|
#include "LowFrequencyOscillator.hpp"
|
|
|
|
namespace Yamaha {
|
|
namespace OPL {
|
|
|
|
/*!
|
|
Opaquely describes the ephemeral state of an operator.
|
|
*/
|
|
struct OperatorState {
|
|
private:
|
|
friend class Operator;
|
|
|
|
int raw_phase_ = 0;
|
|
enum class ADSRPhase {
|
|
Attack, Decay, Sustain, Release
|
|
} adsr_phase_ = ADSRPhase::Attack;
|
|
int adsr_attenuation_ = 511;
|
|
int attack_time_ = 0;
|
|
|
|
int key_level_scaling_;
|
|
int channel_adsr_attenuation_;
|
|
int lfsr_;
|
|
|
|
bool last_key_on_ = false;
|
|
};
|
|
|
|
/*!
|
|
Describes parts of an operator that are genuinely stored per-operator on the OPLL;
|
|
these can be provided to the Operator in order to have it ignore its local values
|
|
if the host is an OPLL or VRC7.
|
|
*/
|
|
struct OperatorOverrides {
|
|
int attenuation = 0;
|
|
bool use_sustain_level = false;
|
|
};
|
|
|
|
/*!
|
|
Models an operator.
|
|
|
|
In Yamaha FM terms, an operator is a combination of a few things:
|
|
|
|
* an oscillator, producing one of a handful of sine-derived waveforms;
|
|
* an ADSR output level envelope; and
|
|
* a bunch of potential adjustments to those two things:
|
|
* optional tremolo and/or vibrato (the rates of which are global);
|
|
* the option to skip 'sustain' in ADSR and go straight to release (since no sustain period is supplied,
|
|
it otherwise runs for as long as the programmer leaves a channel enabled);
|
|
* an attenuation for the output level; and
|
|
* a factor by which to speed up the ADSR envelope as a function of frequency.
|
|
|
|
Oscillator period isn't set directly, it's a multiple of the owning channel, in which
|
|
period is set as a combination of f-num and octave.
|
|
*/
|
|
class Operator {
|
|
public:
|
|
/// Sets this operator's attack rate as the top nibble of @c value, its decay rate as the bottom nibble.
|
|
void set_attack_decay(uint8_t value);
|
|
|
|
/// Sets this operator's sustain level as the top nibble of @c value, its release rate as the bottom nibble.
|
|
void set_sustain_release(uint8_t value);
|
|
|
|
/// Sets this operator's key scale level as the top two bits of @c value, its total output level as the low six bits.
|
|
void set_scaling_output(uint8_t value);
|
|
|
|
/// Sets this operator's waveform using the low two bits of @c value.
|
|
void set_waveform(uint8_t value);
|
|
|
|
/// From the top nibble of @c value sets the AM, vibrato, hold/sustain level and keyboard sampling rate flags;
|
|
/// uses the bottom nibble to set the period multiplier.
|
|
void set_am_vibrato_hold_sustain_ksr_multiple(uint8_t value);
|
|
|
|
/// Provides one clock tick to the operator, along with the relevant parameters of its channel.
|
|
void update(
|
|
OperatorState &state,
|
|
const LowFrequencyOscillator &oscillator,
|
|
bool key_on,
|
|
int channel_period,
|
|
int channel_octave,
|
|
const OperatorOverrides *overrides = nullptr);
|
|
|
|
/// @returns @c true if this channel currently has a non-zero output; @c false otherwise.
|
|
bool is_audible(OperatorState &state, OperatorOverrides *overrides = nullptr);
|
|
|
|
/// Provides ordinary melodic output, optionally with modulation.
|
|
LogSign melodic_output(const OperatorState &state, const LogSign *phase_offset = nullptr);
|
|
|
|
/// Provides snare drum output, which is a function of phase and the captured LFSR level.
|
|
LogSign snare_output(const OperatorState &state);
|
|
|
|
/// Provides cymbal output, which is a function of the phase given by @c state, ordinarily the carrier of channel 8,
|
|
/// and the phase of @c modulator, which is ordinarily the modulator of channel 7.
|
|
LogSign cymbal_output(const OperatorState &state, const OperatorState &modulator);
|
|
|
|
/// Provides high-hat output, which is a function of the phase given by @c state, ordinarily the carrier of channel 8,
|
|
/// and the phase of @c modulator, which is ordinarily the modulator of channel 7.
|
|
LogSign high_hat_output(const OperatorState &state, const OperatorState &modulator);
|
|
|
|
private:
|
|
/// If true then an amplitude modulation of "3.7Hz" is applied,
|
|
/// with a depth "determined by the AM-DEPTH of the BD register"?
|
|
bool apply_amplitude_modulation_ = false;
|
|
|
|
/// If true then a vibrato of '6.4 Hz' is applied, with a depth
|
|
/// "determined by VOB_DEPTH of the BD register"?
|
|
bool apply_vibrato_ = false;
|
|
|
|
/// Selects between an ADSR envelope that holds at the sustain level
|
|
/// for as long as this key is on, releasing afterwards, and one that
|
|
/// simply switches straight to the release rate once the sustain
|
|
/// level is hit, getting back to 0 regardless of an ongoing key-on.
|
|
bool use_sustain_level_ = false;
|
|
|
|
/// Indexes a lookup table to determine what multiple of the channel's frequency
|
|
/// this operator is advancing at.
|
|
int frequency_multiple_ = 0;
|
|
|
|
/// Sets the current output level of this modulator, as an attenuation.
|
|
int attenuation_ = 0;
|
|
|
|
/// Provides a potential faster step through the ADSR envelope. Cf. p12.
|
|
int key_rate_scaling_shift_ = 0;
|
|
|
|
/// Selects attenuation that is applied as a function of interval. Cf. p14.
|
|
int key_level_scaling_ = 0;
|
|
|
|
/// Sets the ADSR rates. These all provide the top four bits of a six-bit number;
|
|
/// the bottom two bits... are 'RL'?
|
|
int attack_rate_ = 0;
|
|
int decay_rate_ = 0;
|
|
int sustain_level_ = 0;
|
|
int release_rate_ = 0;
|
|
|
|
/// Selects the generated waveform.
|
|
enum class Waveform {
|
|
Sine, HalfSine, AbsSine, PulseSine
|
|
} waveform_ = Waveform::Sine;
|
|
|
|
/// Updates the ADSR envelope.
|
|
void update_adsr(
|
|
OperatorState &state,
|
|
const LowFrequencyOscillator &oscillator,
|
|
bool key_on,
|
|
int channel_period,
|
|
int channel_octave,
|
|
const OperatorOverrides *overrides);
|
|
|
|
/// Updates the phase generator.
|
|
void update_phase(OperatorState &state, const LowFrequencyOscillator &oscillator, int channel_period, int channel_octave);
|
|
|
|
/// Adds key-level scaling to the current output state.
|
|
int key_level_scaling(OperatorState &state, int channel_period, int channel_octave);
|
|
|
|
/// Adds ADSR and general channel attenuations to the output state.
|
|
int attenuation_adsr(OperatorState &state, const LowFrequencyOscillator &oscillator, const OperatorOverrides *overrides);
|
|
|
|
};
|
|
|
|
}
|
|
}
|
|
|
|
#endif /* Operator_hpp */
|