mirror of
https://github.com/TomHarte/CLK.git
synced 2025-03-25 21:34:05 +00:00
Attempts completely to decouple updates and audio outputs.
This commit is contained in:
parent
1f34214fb3
commit
c4135fad2b
Components/OPL2
@ -33,30 +33,33 @@ void Channel::set_feedback_mode(uint8_t value) {
|
||||
use_fm_synthesis_ = value & 1;
|
||||
}
|
||||
|
||||
int Channel::update_melodic(const LowFrequencyOscillator &oscillator, Operator *modulator, Operator *carrier, bool force_key_on, OperatorOverrides *modulator_overrides, OperatorOverrides *carrier_overrides) {
|
||||
modulator->update(modulator_state_, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, modulator_overrides);
|
||||
carrier->update(carrier_state_, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, carrier_overrides);
|
||||
void Channel::update(bool modulator, const LowFrequencyOscillator &oscillator, Operator &op, bool force_key_on, OperatorOverrides *overrides) {
|
||||
op.update(states_[int(modulator)], oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, overrides);
|
||||
}
|
||||
|
||||
int Channel::melodic_output(Operator &modulator, Operator &carrier, OperatorOverrides *modulator_overrides, OperatorOverrides *carrier_overrides) {
|
||||
if(use_fm_synthesis_) {
|
||||
// Get modulator level, use that as a phase-adjusting input to the carrier and then return the carrier level.
|
||||
const LogSign modulator_output = modulator->melodic_output(modulator_state_);
|
||||
return carrier->melodic_output(carrier_state_, &modulator_output).level();
|
||||
const LogSign modulator_output = modulator.melodic_output(states_[1]);
|
||||
return carrier.melodic_output(states_[0], &modulator_output).level();
|
||||
} else {
|
||||
// Get modulator and carrier levels separately, return their sum.
|
||||
return (carrier->melodic_output(carrier_state_).level() + modulator->melodic_output(carrier_state_).level()) >> 1;
|
||||
return (carrier.melodic_output(states_[0]).level() + modulator.melodic_output(states_[1]).level()) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
int Channel::update_tom_tom(const LowFrequencyOscillator &oscillator, Operator *modulator, bool force_key_on, OperatorOverrides *modulator_overrides) {
|
||||
modulator->update(modulator_state_, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, modulator_overrides);
|
||||
return modulator->melodic_output(modulator_state_).level();
|
||||
int Channel::tom_tom_output(Operator &modulator, OperatorOverrides *modulator_overrides) {
|
||||
return modulator.melodic_output(states_[1]).level();
|
||||
}
|
||||
|
||||
int Channel::update_snare(const LowFrequencyOscillator &oscillator, Operator *carrier, bool force_key_on, OperatorOverrides *carrier_overrides) {
|
||||
carrier->update(carrier_state_, oscillator, key_on_ || force_key_on, period_ << frequency_shift_, octave_, carrier_overrides);
|
||||
return carrier->snare_output(modulator_state_).level();
|
||||
int Channel::snare_output(Operator &carrier, OperatorOverrides *carrier_overrides) {
|
||||
return carrier.snare_output(states_[0]).level();
|
||||
}
|
||||
|
||||
int Channel::cymbal_output(Operator &modulator, Operator &carrier, Channel &channel8, OperatorOverrides *modulator_overrides) {
|
||||
return carrier.cymbal_output(states_[0], channel8.states_[1]).level();
|
||||
}
|
||||
|
||||
bool Channel::is_audible(Operator *carrier, OperatorOverrides *carrier_overrides) {
|
||||
return carrier->is_audible(carrier_state_, carrier_overrides);
|
||||
return carrier->is_audible(states_[0], carrier_overrides);
|
||||
}
|
||||
|
@ -39,17 +39,23 @@ class Channel {
|
||||
/// associated with this channel, and whether FM synthesis is in use.
|
||||
void set_feedback_mode(uint8_t value);
|
||||
|
||||
/// Updates this channel, using the operators for melodic output.
|
||||
int update_melodic(const LowFrequencyOscillator &oscillator, Operator *modulator, Operator *carrier, bool force_key_on = false, OperatorOverrides *modulator_overrides = nullptr, OperatorOverrides *carrier_overrides = nullptr);
|
||||
/// Updates one of this channel's operators.
|
||||
void update(bool modulator, const LowFrequencyOscillator &oscillator, Operator &op, bool force_key_on = false, OperatorOverrides *overrides = nullptr);
|
||||
|
||||
/// Updates this channel's modulator state, to produce a tom tom.
|
||||
int update_tom_tom(const LowFrequencyOscillator &oscillator, Operator *modulator, bool force_key_on, OperatorOverrides *modulator_overrides = nullptr);
|
||||
/// Gets regular 'melodic' output for this channel, i.e. the output you'd expect from all channels when not in rhythm mode.
|
||||
int melodic_output(Operator &modulator, Operator &carrier, OperatorOverrides *modulator_overrides = nullptr, OperatorOverrides *carrier_overrides = nullptr);
|
||||
|
||||
/// Updates this channel, using the carrier to produce a snare drum and the modulator to produce a tom tom.
|
||||
int update_snare(const LowFrequencyOscillator &oscillator, Operator *carrier, bool force_key_on, OperatorOverrides *carrier_overrides = nullptr);
|
||||
/// Generates tom tom output using this channel's modulator.
|
||||
int tom_tom_output(Operator &modulator, OperatorOverrides *modulator_overrides = nullptr);
|
||||
|
||||
/// Updates this channel, using the carrier to produce a cymbal and the modulator to produce a high-hat.
|
||||
int update_cymbal_high_hat(const LowFrequencyOscillator &oscillator, Operator *modulator, Operator *carrier, OperatorOverrides *modulator_overrides = nullptr, OperatorOverrides *carrier_overrides = nullptr);
|
||||
/// Generates snare output, using this channel's carrier.
|
||||
int snare_output(Operator &carrier, OperatorOverrides *carrier_overrides = nullptr);
|
||||
|
||||
/// Generates cymbal output, using this channel's modulator and @c channel8 's carrier.
|
||||
int cymbal_output(Operator &modulator, Operator &carrier, Channel &channel8, OperatorOverrides *modulator_overrides = nullptr);
|
||||
|
||||
/// Generates cymbal output, using this channel's modulator and @c channel8 's carrier.
|
||||
int high_hat_output(Operator &modulator, Operator &carrier, Channel &channel8, OperatorOverrides *modulator_overrides = nullptr);
|
||||
|
||||
/// @returns @c true if this channel is currently producing any audio; @c false otherwise;
|
||||
bool is_audible(Operator *carrier, OperatorOverrides *carrier_overrides = nullptr);
|
||||
@ -75,9 +81,10 @@ class Channel {
|
||||
/// selections look the same when passed to the operators.
|
||||
int frequency_shift_ = 0;
|
||||
|
||||
// Stored separately because carrier/modulator may not be unique per channel —
|
||||
// Operator state is stored distinctly from Operators because
|
||||
// carrier/modulator may not be unique per channel —
|
||||
// on the OPLL there's an extra level of indirection.
|
||||
OperatorState carrier_state_, modulator_state_;
|
||||
OperatorState states_[2];
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -68,8 +68,6 @@ void OPLL::get_samples(std::size_t number_of_samples, std::int16_t *target) {
|
||||
|
||||
while(number_of_samples--) {
|
||||
if(!audio_offset_) update_all_chanels();
|
||||
if(!(audio_offset_&3))
|
||||
oscillator_.update_lfsr();
|
||||
|
||||
*target = int16_t(output_levels_[audio_offset_ / channel_output_period]);
|
||||
++target;
|
||||
@ -193,61 +191,63 @@ void OPLL::setup_fixed_instrument(int number, const uint8_t *data) {
|
||||
}
|
||||
|
||||
void OPLL::update_all_chanels() {
|
||||
// Update the LFO.
|
||||
// Update the LFO and then the channels.
|
||||
oscillator_.update();
|
||||
for(int c = 0; c < 6; ++c) {
|
||||
channels_[c].update(oscillator_);
|
||||
oscillator_.update_lfsr(); // TODO: should update per slot, not per channel? Or even per cycle?
|
||||
}
|
||||
|
||||
int channel_levels[9];
|
||||
output_levels_[8] = output_levels_[12] = 0;
|
||||
|
||||
#define VOLUME(x) ((x) * total_volume_) >> 11
|
||||
#define VOLUME(x) ((x) * total_volume_) >> 12
|
||||
|
||||
// Channels that are updated for melodic output regardless;
|
||||
// in rhythm mode the final three channels — 6, 7, and 8 —
|
||||
// are lost as their operators are used for drum noises.
|
||||
for(int c = 0; c < 6; ++ c) {
|
||||
channel_levels[c] = VOLUME(channels_[c].update_melodic(oscillator_));
|
||||
}
|
||||
output_levels_[3] = VOLUME(channels_[0].melodic_output());
|
||||
output_levels_[4] = VOLUME(channels_[1].melodic_output());
|
||||
output_levels_[5] = VOLUME(channels_[2].melodic_output());
|
||||
|
||||
output_levels_[3] = channel_levels[0];
|
||||
output_levels_[4] = channel_levels[1];
|
||||
output_levels_[5] = channel_levels[2];
|
||||
output_levels_[9] = channel_levels[3];
|
||||
output_levels_[10] = channel_levels[4];
|
||||
output_levels_[11] = channel_levels[5];
|
||||
output_levels_[9] = VOLUME(channels_[3].melodic_output());
|
||||
output_levels_[10] = VOLUME(channels_[4].melodic_output());
|
||||
output_levels_[11] = VOLUME(channels_[5].melodic_output());
|
||||
|
||||
if(depth_rhythm_control_ & 0x20) {
|
||||
// Rhythm mode.
|
||||
output_levels_[8] = output_levels_[12] = 0;
|
||||
// TODO: pervasively, volume. And LFSR updates.
|
||||
|
||||
// Update channel 6 as if melodic, but with the bass instrument.
|
||||
output_levels_[2] = output_levels_[15] =
|
||||
VOLUME(channels_[6].update_bass(oscillator_, &operators_[32], depth_rhythm_control_ & 0x10));
|
||||
channels_[6].update(oscillator_, &operators_[32], depth_rhythm_control_ & 0x10);
|
||||
output_levels_[2] = output_levels_[15] = VOLUME(channels_[6].melodic_output());
|
||||
|
||||
// Use the modulator from channel 7 for the tom tom.
|
||||
output_levels_[1] = output_levels_[14] =
|
||||
VOLUME(channels_[7].update_tom_tom(oscillator_, &operators_[34], depth_rhythm_control_ & 0x04));
|
||||
channels_[7].update(true, oscillator_, operators_[34], bool(depth_rhythm_control_ & 0x04));
|
||||
output_levels_[1] = output_levels_[14] = VOLUME(channels_[7].tom_tom_output(operators_[34]));
|
||||
|
||||
// Use the carrier from channel 7 for the snare.
|
||||
output_levels_[6] = output_levels_[16] =
|
||||
VOLUME(channels_[7].update_snare(oscillator_, &operators_[35], depth_rhythm_control_ & 0x08));
|
||||
channels_[7].update(false, oscillator_, operators_[35], bool(depth_rhythm_control_ & 0x08));
|
||||
output_levels_[6] = output_levels_[16] = VOLUME(channels_[7].snare_output(operators_[35]));
|
||||
|
||||
// TODO: cymbal.
|
||||
output_levels_[7] = output_levels_[17] = 0;
|
||||
// Use the channel 7 modulator and the channel 8 carrier for a cymbal.
|
||||
channels_[8].update(false, oscillator_, operators_[36], bool(depth_rhythm_control_ & 0x01));
|
||||
output_levels_[7] = output_levels_[17] = VOLUME(channels_[7].cymbal_output(operators_[36], operators_[35], channels_[8]));
|
||||
|
||||
// TODO: high hat.
|
||||
output_levels_[0] = output_levels_[13] = 0;
|
||||
} else {
|
||||
// Not in rhythm mode; channels 7, 8 and 9 are melodic.
|
||||
for(int c = 7; c < 9; ++ c) {
|
||||
channel_levels[c] = VOLUME(channels_[c].update_melodic(oscillator_));
|
||||
for(int c = 6; c < 9; ++ c) {
|
||||
channels_[c].update(oscillator_);
|
||||
oscillator_.update_lfsr(); // TODO: should update per slot, not per channel? Or even per cycle?
|
||||
}
|
||||
|
||||
output_levels_[0] = output_levels_[1] = output_levels_[2] =
|
||||
output_levels_[6] = output_levels_[7] = output_levels_[8] =
|
||||
output_levels_[12] = output_levels_[13] = output_levels_[14] = 0;
|
||||
output_levels_[6] = output_levels_[7] =
|
||||
output_levels_[13] = output_levels_[14] = 0;
|
||||
|
||||
output_levels_[15] = channel_levels[3];
|
||||
output_levels_[16] = channel_levels[4];
|
||||
output_levels_[17] = channel_levels[5];
|
||||
output_levels_[15] = VOLUME(channels_[6].melodic_output());
|
||||
output_levels_[16] = VOLUME(channels_[7].melodic_output());
|
||||
output_levels_[17] = VOLUME(channels_[8].melodic_output());
|
||||
}
|
||||
|
||||
// Test!
|
||||
|
@ -91,19 +91,31 @@ struct OPLL: public OPLBase<OPLL> {
|
||||
|
||||
|
||||
struct Channel: public ::Yamaha::OPL::Channel {
|
||||
int update_melodic(const LowFrequencyOscillator &oscillator) {
|
||||
return Yamaha::OPL::Channel::update_melodic(oscillator, modulator, modulator + 1, false, nullptr, &overrides);
|
||||
void update(const LowFrequencyOscillator &oscillator) {
|
||||
Yamaha::OPL::Channel::update(true, oscillator, modulator[0]);
|
||||
Yamaha::OPL::Channel::update(false, oscillator, modulator[1], false, &overrides);
|
||||
}
|
||||
|
||||
int update_bass(const LowFrequencyOscillator &oscillator, Operator *bass, bool key_on) {
|
||||
return Yamaha::OPL::Channel::update_melodic(oscillator, bass, bass + 1, key_on, nullptr, &overrides);
|
||||
void update(const LowFrequencyOscillator &oscillator, Operator *mod, bool key_on) {
|
||||
Yamaha::OPL::Channel::update(true, oscillator, mod[0], key_on);
|
||||
Yamaha::OPL::Channel::update(false, oscillator, mod[1], key_on, &overrides);
|
||||
}
|
||||
|
||||
int update_tom_tom(const LowFrequencyOscillator &oscillator, Operator *bass, bool key_on) {
|
||||
// TODO: should overrides be applied here?
|
||||
return Yamaha::OPL::Channel::update_tom_tom(oscillator, bass, key_on, &overrides);
|
||||
using ::Yamaha::OPL::Channel::update;
|
||||
|
||||
int melodic_output() {
|
||||
return Yamaha::OPL::Channel::melodic_output(modulator[0], modulator[1]);
|
||||
}
|
||||
|
||||
// int update_bass(const LowFrequencyOscillator &oscillator, Operator *bass, bool key_on) {
|
||||
// return Yamaha::OPL::Channel::update_melodic(oscillator, bass, bass + 1, key_on, nullptr, &overrides);
|
||||
// }
|
||||
//
|
||||
// int update_tom_tom(const LowFrequencyOscillator &oscillator, Operator *bass, bool key_on) {
|
||||
// // TODO: should overrides be applied here?
|
||||
// return Yamaha::OPL::Channel::update_tom_tom(oscillator, bass, key_on, &overrides);
|
||||
// }
|
||||
|
||||
bool is_audible() {
|
||||
return Yamaha::OPL::Channel::is_audible(modulator + 1, &overrides);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user