mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
In principle fully implements rhythm mode.
This commit is contained in:
parent
e12dc5d894
commit
5219a86a41
@ -32,6 +32,36 @@ OPLL::OPLL(Concurrency::DeferringAsyncTaskQueue &task_queue, int audio_divider,
|
|||||||
for(int c = 0; c < 9; ++c) {
|
for(int c = 0; c < 9; ++c) {
|
||||||
install_instrument(c);
|
install_instrument(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup the rhythm envelope generators.
|
||||||
|
|
||||||
|
// Treat the bass exactly as if it were a melodic channel.
|
||||||
|
rhythm_envelope_generators_[BassCarrier].set_should_damp([this] {
|
||||||
|
// Propagate attack mode to the modulator, and reset both phases.
|
||||||
|
rhythm_envelope_generators_[BassModulator].set_key_on(true);
|
||||||
|
phase_generators_[6 + 0].reset();
|
||||||
|
phase_generators_[6 + 9].reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Crib the proper rhythm envelope generator settings by installing
|
||||||
|
// the rhythm instruments and copying them over.
|
||||||
|
rhythm_mode_enabled_ = true;
|
||||||
|
install_instrument(6);
|
||||||
|
install_instrument(7);
|
||||||
|
install_instrument(8);
|
||||||
|
|
||||||
|
rhythm_envelope_generators_[BassCarrier] = envelope_generators_[6];
|
||||||
|
rhythm_envelope_generators_[BassModulator] = envelope_generators_[6 + 9];
|
||||||
|
rhythm_envelope_generators_[HighHat] = envelope_generators_[7];
|
||||||
|
rhythm_envelope_generators_[Cymbal] = envelope_generators_[8];
|
||||||
|
rhythm_envelope_generators_[TomTom] = envelope_generators_[8 + 9];
|
||||||
|
rhythm_envelope_generators_[Snare] = envelope_generators_[7 + 9];
|
||||||
|
|
||||||
|
// Return to ordinary default mode.
|
||||||
|
rhythm_mode_enabled_ = false;
|
||||||
|
install_instrument(6);
|
||||||
|
install_instrument(7);
|
||||||
|
install_instrument(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Machine-facing programmatic input.
|
// MARK: - Machine-facing programmatic input.
|
||||||
@ -67,11 +97,17 @@ void OPLL::write_register(uint8_t address, uint8_t value) {
|
|||||||
install_instrument(7);
|
install_instrument(7);
|
||||||
install_instrument(8);
|
install_instrument(8);
|
||||||
}
|
}
|
||||||
rhythm_generators_[0].set_key_on(value & 0x01);
|
rhythm_envelope_generators_[0].set_key_on(value & 0x01);
|
||||||
rhythm_generators_[1].set_key_on(value & 0x02);
|
rhythm_envelope_generators_[1].set_key_on(value & 0x02);
|
||||||
rhythm_generators_[2].set_key_on(value & 0x04);
|
rhythm_envelope_generators_[2].set_key_on(value & 0x04);
|
||||||
rhythm_generators_[3].set_key_on(value & 0x08);
|
rhythm_envelope_generators_[3].set_key_on(value & 0x08);
|
||||||
rhythm_generators_[4].set_key_on(value & 0x10);
|
if(value & 0x10) {
|
||||||
|
rhythm_envelope_generators_[4].set_key_on(true);
|
||||||
|
} else {
|
||||||
|
rhythm_envelope_generators_[4].set_key_on(false);
|
||||||
|
rhythm_envelope_generators_[5].set_key_on(false);
|
||||||
|
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,10 +305,8 @@ void OPLL::update_all_channels() {
|
|||||||
|
|
||||||
if(rhythm_mode_enabled_) {
|
if(rhythm_mode_enabled_) {
|
||||||
// Advance the rhythm envelope generators.
|
// Advance the rhythm envelope generators.
|
||||||
// TODO: these need to be properly seeded.
|
for(int c = 0; c < 6; ++c) {
|
||||||
for(int c = 0; c < 5; ++c) {
|
rhythm_envelope_generators_[c].update(oscillator_);
|
||||||
oscillator_.update_lfsr();
|
|
||||||
rhythm_generators_[c].update(oscillator_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill in the melodic channels.
|
// Fill in the melodic channels.
|
||||||
@ -284,12 +318,29 @@ void OPLL::update_all_channels() {
|
|||||||
output_levels_[10] = VOLUME(melodic_output(4));
|
output_levels_[10] = VOLUME(melodic_output(4));
|
||||||
output_levels_[11] = VOLUME(melodic_output(5));
|
output_levels_[11] = VOLUME(melodic_output(5));
|
||||||
|
|
||||||
// TODO: drum noises. Also subject to proper channel population.
|
// Bass drum, which is a regular FM effect.
|
||||||
|
output_levels_[2] = output_levels_[15] = VOLUME(bass_drum());
|
||||||
|
oscillator_.update_lfsr();
|
||||||
|
|
||||||
output_levels_[0] = output_levels_[1] = output_levels_[2] =
|
// Tom tom, which is a single operator.
|
||||||
output_levels_[6] = output_levels_[7] = output_levels_[8] =
|
output_levels_[1] = output_levels_[14] = VOLUME(tom_tom());
|
||||||
output_levels_[12] = output_levels_[13] = output_levels_[14] =
|
oscillator_.update_lfsr();
|
||||||
output_levels_[15] = output_levels_[16] = output_levels_[17] = 0;
|
|
||||||
|
// Snare.
|
||||||
|
output_levels_[6] = output_levels_[16] = VOLUME(snare_drum());
|
||||||
|
oscillator_.update_lfsr();
|
||||||
|
|
||||||
|
// Cymbal.
|
||||||
|
output_levels_[7] = output_levels_[17] = VOLUME(cymbal());
|
||||||
|
oscillator_.update_lfsr();
|
||||||
|
|
||||||
|
// High-hat.
|
||||||
|
output_levels_[0] = output_levels_[13] = VOLUME(high_hat());
|
||||||
|
oscillator_.update_lfsr();
|
||||||
|
|
||||||
|
// Unutilised slots.
|
||||||
|
output_levels_[8] = output_levels_[12] = 0;
|
||||||
|
oscillator_.update_lfsr();
|
||||||
} else {
|
} else {
|
||||||
for(int c = 6; c < 9; ++c) {
|
for(int c = 6; c < 9; ++c) {
|
||||||
envelope_generators_[c + 0].update(oscillator_);
|
envelope_generators_[c + 0].update(oscillator_);
|
||||||
@ -312,8 +363,6 @@ void OPLL::update_all_channels() {
|
|||||||
output_levels_[15] = VOLUME(melodic_output(6));
|
output_levels_[15] = VOLUME(melodic_output(6));
|
||||||
output_levels_[16] = VOLUME(melodic_output(7));
|
output_levels_[16] = VOLUME(melodic_output(7));
|
||||||
output_levels_[17] = VOLUME(melodic_output(8));
|
output_levels_[17] = VOLUME(melodic_output(8));
|
||||||
|
|
||||||
// TODO: advance LFSR.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef VOLUME
|
#undef VOLUME
|
||||||
@ -322,9 +371,9 @@ void OPLL::update_all_channels() {
|
|||||||
// TODO: modulator feedback.
|
// TODO: modulator feedback.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: verify attenuation scales pervasively below.
|
||||||
|
|
||||||
int OPLL::melodic_output(int channel) {
|
int OPLL::melodic_output(int channel) {
|
||||||
// TODO: verify attenuation scales.
|
|
||||||
auto modulation = WaveformGenerator<period_precision>::wave(channels_[channel].modulator_waveform, phase_generators_[channel + 9].phase());
|
auto modulation = WaveformGenerator<period_precision>::wave(channels_[channel].modulator_waveform, phase_generators_[channel + 9].phase());
|
||||||
modulation += envelope_generators_[channel + 9].attenuation() + (channels_[channel].modulator_attenuation << 5) + key_level_scalers_[channel + 9].attenuation();
|
modulation += envelope_generators_[channel + 9].attenuation() + (channels_[channel].modulator_attenuation << 5) + key_level_scalers_[channel + 9].attenuation();
|
||||||
|
|
||||||
@ -332,3 +381,41 @@ int OPLL::melodic_output(int channel) {
|
|||||||
carrier += envelope_generators_[channel].attenuation() + (channels_[channel].attenuation << 7) + key_level_scalers_[channel].attenuation();
|
carrier += envelope_generators_[channel].attenuation() + (channels_[channel].attenuation << 7) + key_level_scalers_[channel].attenuation();
|
||||||
return carrier.level();
|
return carrier.level();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int OPLL::bass_drum() {
|
||||||
|
// Use modulator 6 and carrier 6, attenuated as per the bass-specific envelope generators and the attenuation level for channel 6.
|
||||||
|
auto modulation = WaveformGenerator<period_precision>::wave(Waveform::Sine, phase_generators_[6 + 9].phase());
|
||||||
|
modulation += envelope_generators_[RhythmIndices::BassModulator].attenuation();
|
||||||
|
|
||||||
|
auto carrier = WaveformGenerator<period_precision>::wave(Waveform::Sine, phase_generators_[6].scaled_phase(), modulation);
|
||||||
|
carrier += envelope_generators_[RhythmIndices::BassCarrier].attenuation() + (channels_[6].attenuation << 7);
|
||||||
|
return carrier.level();
|
||||||
|
}
|
||||||
|
|
||||||
|
int OPLL::tom_tom() {
|
||||||
|
// Use modulator 8 and the 'instrument' selection for channel 8 as an attenuation.
|
||||||
|
auto tom_tom = WaveformGenerator<period_precision>::wave(Waveform::Sine, phase_generators_[8 + 9].phase());
|
||||||
|
tom_tom += envelope_generators_[RhythmIndices::TomTom].attenuation() + (channels_[8].instrument << 7);
|
||||||
|
return tom_tom.level();
|
||||||
|
}
|
||||||
|
|
||||||
|
int OPLL::snare_drum() {
|
||||||
|
// Use modulator 7 and the carrier attenuation level for channel 7.
|
||||||
|
LogSign snare = WaveformGenerator<period_precision>::snare(oscillator_, phase_generators_[7 + 9].phase());
|
||||||
|
snare += channels_[7].attenuation << 7;
|
||||||
|
return snare.level();
|
||||||
|
}
|
||||||
|
|
||||||
|
int OPLL::cymbal() {
|
||||||
|
// Use modulator 7, carrier 8 and the attenuation level for channel 8.
|
||||||
|
LogSign cymbal = WaveformGenerator<period_precision>::cymbal(phase_generators_[8].phase(), phase_generators_[7 + 9].phase());
|
||||||
|
cymbal += channels_[8].attenuation << 7;
|
||||||
|
return cymbal.level();
|
||||||
|
}
|
||||||
|
|
||||||
|
int OPLL::high_hat() {
|
||||||
|
// Use the 'instrument' selection for channel 7 as an attenuation.
|
||||||
|
LogSign high_hat = WaveformGenerator<period_precision>::high_hat(oscillator_, phase_generators_[8].phase(), phase_generators_[7 + 9].phase());
|
||||||
|
high_hat += channels_[7].instrument << 7;
|
||||||
|
return high_hat.level();
|
||||||
|
}
|
||||||
|
@ -41,21 +41,40 @@ class OPLL: public OPLBase<OPLL> {
|
|||||||
int audio_offset_ = 0;
|
int audio_offset_ = 0;
|
||||||
std::atomic<int> total_volume_;
|
std::atomic<int> total_volume_;
|
||||||
|
|
||||||
void update_all_channels();
|
|
||||||
int melodic_output(int channel);
|
|
||||||
int16_t output_levels_[18];
|
int16_t output_levels_[18];
|
||||||
|
void update_all_channels();
|
||||||
|
|
||||||
|
int melodic_output(int channel);
|
||||||
|
int bass_drum();
|
||||||
|
int tom_tom();
|
||||||
|
int snare_drum();
|
||||||
|
int cymbal();
|
||||||
|
int high_hat();
|
||||||
|
|
||||||
static constexpr int period_precision = 9;
|
static constexpr int period_precision = 9;
|
||||||
static constexpr int envelope_precision = 9;
|
static constexpr int envelope_precision = 9;
|
||||||
|
|
||||||
// Standard melodic phase and envelope generators.
|
// Standard melodic phase and envelope generators;
|
||||||
|
//
|
||||||
|
// These are assigned as:
|
||||||
|
//
|
||||||
|
// [x], 0 <= x < 9 = carrier for channel x;
|
||||||
|
// [x+9] = modulator for channel x.
|
||||||
|
//
|
||||||
PhaseGenerator<period_precision> phase_generators_[18];
|
PhaseGenerator<period_precision> phase_generators_[18];
|
||||||
EnvelopeGenerator<envelope_precision, period_precision> envelope_generators_[18];
|
EnvelopeGenerator<envelope_precision, period_precision> envelope_generators_[18];
|
||||||
KeyLevelScaler<period_precision> key_level_scalers_[18];
|
KeyLevelScaler<period_precision> key_level_scalers_[18];
|
||||||
|
|
||||||
// Dedicated rhythm envelope generators and attenuations.
|
// Dedicated rhythm envelope generators and attenuations.
|
||||||
EnvelopeGenerator<envelope_precision, period_precision> rhythm_generators_[5];
|
EnvelopeGenerator<envelope_precision, period_precision> rhythm_envelope_generators_[6];
|
||||||
int rhythm_attenuations_[5];
|
enum RhythmIndices {
|
||||||
|
HighHat = 0,
|
||||||
|
Cymbal = 1,
|
||||||
|
TomTom = 2,
|
||||||
|
Snare = 3,
|
||||||
|
BassCarrier = 4,
|
||||||
|
BassModulator = 5
|
||||||
|
};
|
||||||
|
|
||||||
// Channel specifications.
|
// Channel specifications.
|
||||||
struct Channel {
|
struct Channel {
|
||||||
|
Loading…
Reference in New Issue
Block a user