1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-11 15:30:52 +00:00

Adds waveform generation logic to the new factoring.

This commit is contained in:
Thomas Harte 2020-05-03 21:38:20 -04:00
parent 1ff5ea0a6e
commit 1223c99e0f
5 changed files with 104 additions and 1 deletions

View File

@ -27,7 +27,7 @@ template <int frequency_precision> class KeyLevelScaler {
// on the sign bit to clamp to zero. // on the sign bit to clamp to zero.
level_ = key_level_scales[period >> (frequency_precision - 4)]; level_ = key_level_scales[period >> (frequency_precision - 4)];
level_ -= 16 * (octave ^ 7); level_ -= 16 * (octave ^ 7);
level_ &= masks[(key_scale_rate_ >> ((sizeof(int) * 8) - 1)) & 1]; level_ &= masks[(level_ >> ((sizeof(int) * 8) - 1)) & 1];
} }
/*! /*!

View File

@ -51,6 +51,13 @@ template <int precision> class PhaseGenerator {
return phase_ >> precision_shift; return phase_ >> precision_shift;
} }
/*!
@returns Current phase, scaled up by (1 << precision).
*/
int scaled_phase() const {
return phase_ >> 1;
}
/*! /*!
Sets the multiple for this phase generator, in the same terms as an OPL programmer, Sets the multiple for this phase generator, in the same terms as an OPL programmer,
i.e. a 4-bit number that is used as a lookup into the internal multiples table. i.e. a 4-bit number that is used as a lookup into the internal multiples table.

View File

@ -0,0 +1,92 @@
//
// WaveformGenerator.hpp
// Clock Signal
//
// Created by Thomas Harte on 03/05/2020.
// Copyright © 2020 Thomas Harte. All rights reserved.
//
#ifndef WaveformGenerator_h
#define WaveformGenerator_h
#include "Tables.hpp"
#include "LowFrequencyOscillator.hpp"
namespace Yamaha {
namespace OPL {
template <int phase_precision> class WaveformGenerator {
public:
enum class Waveform {
Sine, HalfSine, AbsSine, PulseSine
};
/*!
@returns The output of waveform @c form at [integral] phase @c phase.
*/
static constexpr LogSign wave(Waveform form, int phase) {
constexpr int waveforms[4][4] = {
{1023, 1023, 1023, 1023}, // Sine: don't mask in any quadrant.
{511, 511, 0, 0}, // Half sine: keep the first half intact, lock to 0 in the second half.
{511, 511, 511, 511}, // AbsSine: endlessly repeat the first half of the sine wave.
{255, 0, 255, 0}, // PulseSine: act as if the first quadrant is in the first and third; lock the other two to 0.
};
return negative_log_sin(phase & waveforms[int(form)][(phase >> 8) & 3]);
}
/*!
@returns The output of waveform @c form at [scaled] phase @c scaled_phase given the modulation input @c modulation.
*/
static constexpr LogSign wave(Waveform form, int scaled_phase, LogSign modulation) {
const int scaled_phase_offset = modulation.level(phase_precision);
const int phase = (scaled_phase + scaled_phase_offset) >> phase_precision;
return wave(form, phase);
}
/*!
@returns Snare output, calculated from the current LFSR state as captured in @c oscillator and an operator's phase.
*/
static constexpr LogSign snare(const LowFrequencyOscillator &oscillator, int phase) {
// If noise is 0, output is positive.
// If noise is 1, output is negative.
// If (noise ^ sign) is 0, output is 0. Otherwise it is max.
const int sign = phase & 0x200;
const int level = ((phase >> 9) & 1) ^ oscillator.lfsr;
return negative_log_sin(sign + (level << 8));
}
/*!
@returns Cymbal output, calculated from an operator's phase and a modulator's phase.
*/
static constexpr LogSign cymbal(int carrier_phase, int modulator_phase) {
return negative_log_sin(256 + (phase_combination(carrier_phase, modulator_phase) << 9));
}
/*!
@returns High-hat output, calculated from the current LFSR state as captured in @c oscillator, an operator's phase and a modulator's phase.
*/
static constexpr LogSign high_hat(const LowFrequencyOscillator &oscillator, int carrier_phase, int modulator_phase) {
constexpr int angles[] = {0x234, 0xd0, 0x2d0, 0x34};
return negative_log_sin(angles[
phase_combination(carrier_phase, modulator_phase) |
(oscillator.lfsr << 1)
]);
}
private:
/*!
@returns The phase bit used for cymbal and high-hat generation, which is a function of two operators' phases.
*/
static constexpr int phase_combination(int carrier_phase, int modulator_phase) {
return (
((carrier_phase >> 5) ^ (carrier_phase >> 3)) &
((modulator_phase >> 7) ^ (modulator_phase >> 2)) &
((carrier_phase >> 5) ^ (modulator_phase >> 3))
) & 1;
}
};
}
}
#endif /* WaveformGenerator_h */

View File

@ -13,6 +13,8 @@
#include "Implementation/PhaseGenerator.hpp" #include "Implementation/PhaseGenerator.hpp"
#include "Implementation/EnvelopeGenerator.hpp" #include "Implementation/EnvelopeGenerator.hpp"
#include "Implementation/KeyLevelScaler.hpp"
#include "Implementation/WaveformGenerator.hpp"
using namespace Yamaha::OPL; using namespace Yamaha::OPL;

View File

@ -1154,6 +1154,7 @@
4B619092245BC1000013F202 /* PhaseGenerator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PhaseGenerator.hpp; sourceTree = "<group>"; }; 4B619092245BC1000013F202 /* PhaseGenerator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PhaseGenerator.hpp; sourceTree = "<group>"; };
4B619093245CD63E0013F202 /* EnvelopeGenerator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EnvelopeGenerator.hpp; sourceTree = "<group>"; }; 4B619093245CD63E0013F202 /* EnvelopeGenerator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EnvelopeGenerator.hpp; sourceTree = "<group>"; };
4B619094245E73B90013F202 /* KeyLevelScaler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyLevelScaler.hpp; sourceTree = "<group>"; }; 4B619094245E73B90013F202 /* KeyLevelScaler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KeyLevelScaler.hpp; sourceTree = "<group>"; };
4B619095245FA04B0013F202 /* WaveformGenerator.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = WaveformGenerator.hpp; sourceTree = "<group>"; };
4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DisplayMetrics.cpp; path = ../../Outputs/DisplayMetrics.cpp; sourceTree = "<group>"; }; 4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DisplayMetrics.cpp; path = ../../Outputs/DisplayMetrics.cpp; sourceTree = "<group>"; };
4B622AE4222E0AD5008B59F2 /* DisplayMetrics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DisplayMetrics.hpp; path = ../../Outputs/DisplayMetrics.hpp; sourceTree = "<group>"; }; 4B622AE4222E0AD5008B59F2 /* DisplayMetrics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DisplayMetrics.hpp; path = ../../Outputs/DisplayMetrics.hpp; sourceTree = "<group>"; };
4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; }; 4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; };
@ -3538,6 +3539,7 @@
4B619092245BC1000013F202 /* PhaseGenerator.hpp */, 4B619092245BC1000013F202 /* PhaseGenerator.hpp */,
4B619093245CD63E0013F202 /* EnvelopeGenerator.hpp */, 4B619093245CD63E0013F202 /* EnvelopeGenerator.hpp */,
4B619094245E73B90013F202 /* KeyLevelScaler.hpp */, 4B619094245E73B90013F202 /* KeyLevelScaler.hpp */,
4B619095245FA04B0013F202 /* WaveformGenerator.hpp */,
); );
path = Implementation; path = Implementation;
sourceTree = "<group>"; sourceTree = "<group>";