mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-02 16:04:59 +00:00
Via a unit test, confirms and fixes relative volumes of OPLL channels.
Also rejigs responsibility for scaling to emulator-standard volume.
This commit is contained in:
parent
39e6a28730
commit
32fd1897d0
@ -40,12 +40,12 @@ int Channel::update(Operator *modulator, Operator *carrier, OperatorOverrides *m
|
|||||||
const auto modulator_level = modulator_state_.level();
|
const auto modulator_level = modulator_state_.level();
|
||||||
|
|
||||||
carrier->update(carrier_state_, key_on_, period_ << frequency_shift_, octave_, modulator_level, carrier_overrides);
|
carrier->update(carrier_state_, key_on_, period_ << frequency_shift_, octave_, modulator_level, carrier_overrides);
|
||||||
return carrier_state_.level() << 2;
|
return carrier_state_.level();
|
||||||
} else {
|
} else {
|
||||||
// Get modulator and carrier levels separately, return their sum.
|
// Get modulator and carrier levels separately, return their sum.
|
||||||
modulator->update(modulator_state_, key_on_, period_ << frequency_shift_, octave_, 0, modulator_overrides);
|
modulator->update(modulator_state_, key_on_, period_ << frequency_shift_, octave_, 0, modulator_overrides);
|
||||||
carrier->update(carrier_state_, key_on_, period_ << frequency_shift_, octave_, 0, carrier_overrides);
|
carrier->update(carrier_state_, key_on_, period_ << frequency_shift_, octave_, 0, carrier_overrides);
|
||||||
return (modulator_state_.level() + carrier_state_.level()) << 1;
|
return (modulator_state_.level() + carrier_state_.level());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ void Operator::update(OperatorState &state, bool key_on, int channel_period, int
|
|||||||
// Overrides here represent per-channel volume on an OPLL. The bits are defined to represent
|
// Overrides here represent per-channel volume on an OPLL. The bits are defined to represent
|
||||||
// attenuations of 24db to 3db; the main envelope generator is stated to have a resolution of
|
// attenuations of 24db to 3db; the main envelope generator is stated to have a resolution of
|
||||||
// 0.325db (which I've assumed is supposed to say 0.375db).
|
// 0.325db (which I've assumed is supposed to say 0.375db).
|
||||||
state.attenuation.log += state.adsr_attenuation_ + (overrides->attenuation << 4);
|
state.attenuation.log += (state.adsr_attenuation_ << 3) + (overrides->attenuation << 7);
|
||||||
} else {
|
} else {
|
||||||
// Overrides here represent per-channel volume on an OPLL. The bits are defined to represent
|
// Overrides here represent per-channel volume on an OPLL. The bits are defined to represent
|
||||||
// attenuations of 24db to 0.75db.
|
// attenuations of 24db to 0.75db.
|
||||||
|
@ -182,7 +182,7 @@ void OPLL::setup_fixed_instrument(int number, const uint8_t *data) {
|
|||||||
void OPLL::update_all_chanels() {
|
void OPLL::update_all_chanels() {
|
||||||
// Channels that are updated for melodic output regardless.
|
// Channels that are updated for melodic output regardless.
|
||||||
for(int c = 0; c < 6; ++ c) {
|
for(int c = 0; c < 6; ++ c) {
|
||||||
channels_[c].level = (channels_[c].update() * total_volume_) >> 14;
|
channels_[c].level = (channels_[c].update() * total_volume_) >> 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(depth_rhythm_control_ & 0x20) {
|
if(depth_rhythm_control_ & 0x20) {
|
||||||
@ -190,7 +190,7 @@ void OPLL::update_all_chanels() {
|
|||||||
} else {
|
} else {
|
||||||
// All melody, all the time.
|
// All melody, all the time.
|
||||||
for(int c = 6; c < 9; ++ c) {
|
for(int c = 6; c < 9; ++ c) {
|
||||||
channels_[c].level = (channels_[c].update() * total_volume_) >> 14;
|
channels_[c].level = (channels_[c].update() * total_volume_) >> 11;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Release"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
enableASanStackUseAfterReturn = "YES"
|
enableASanStackUseAfterReturn = "YES"
|
||||||
|
@ -68,7 +68,7 @@
|
|||||||
NSEnumerator *goodValues = [knownGood objectEnumerator];
|
NSEnumerator *goodValues = [knownGood objectEnumerator];
|
||||||
for(int c = 0; c < 16384; ++c) {
|
for(int c = 0; c < 16384; ++c) {
|
||||||
const int generated = channel.update(&modulator, &carrier);
|
const int generated = channel.update(&modulator, &carrier);
|
||||||
const int known = [[goodValues nextObject] intValue];
|
const int known = [[goodValues nextObject] intValue] >> 2;
|
||||||
XCTAssertLessThanOrEqual(abs(generated - known), 10, "FM synthesis varies by more than 10 at sample %d of attenuation %d", c, attenuation);
|
XCTAssertLessThanOrEqual(abs(generated - known), 10, "FM synthesis varies by more than 10 at sample %d of attenuation %d", c, attenuation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,6 +87,59 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Level tests
|
||||||
|
|
||||||
|
- (int)maxLevelForOPLLAttenuation:(int)attenuation {
|
||||||
|
Yamaha::OPL::Operator modulator, carrier;
|
||||||
|
Yamaha::OPL::Channel channel;
|
||||||
|
Yamaha::OPL::OperatorOverrides overrides;
|
||||||
|
|
||||||
|
// Reach maximum volume immediately, and hold it during sustain.
|
||||||
|
carrier.set_sustain_release(0x0f);
|
||||||
|
carrier.set_attack_decay(0xf0);
|
||||||
|
|
||||||
|
// Use FM synthesis.
|
||||||
|
channel.set_feedback_mode(1);
|
||||||
|
|
||||||
|
// Set hold sustain level.
|
||||||
|
carrier.set_am_vibrato_hold_sustain_ksr_multiple(0x20);
|
||||||
|
|
||||||
|
// Disable the modulator.
|
||||||
|
modulator.set_scaling_output(0x3f);
|
||||||
|
|
||||||
|
// Set a non-zero frequency, set key on.
|
||||||
|
channel.set_frequency_low(0x40);
|
||||||
|
channel.set_9bit_frequency_octave_key_on(0x10);
|
||||||
|
|
||||||
|
// Get the maximum output for this volume level.
|
||||||
|
overrides.attenuation = attenuation;
|
||||||
|
overrides.use_sustain_level = true;
|
||||||
|
|
||||||
|
int max = 0;
|
||||||
|
for(int c = 0; c < 16384; ++c) {
|
||||||
|
const int level = channel.update(&modulator, &carrier, nullptr, &overrides);
|
||||||
|
if(level > max) max = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testOPLLVolumeLevels {
|
||||||
|
// Get maximum output levels for all channels.
|
||||||
|
int maxLevels[16];
|
||||||
|
for(int c = 0; c < 16; ++c) {
|
||||||
|
maxLevels[c] = [self maxLevelForOPLLAttenuation:c];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out a divider to turn that into the sampled range.
|
||||||
|
const double multiplier = 255.0 / double(maxLevels[0]);
|
||||||
|
const double expectedLevels[16] = {255.0, 181.0, 127.0, 90.0, 63.0, 45.0, 31.0, 22.0, 15.0, 11.0, 7.0, 5.0, 3.0, 2.0, 1.0, 1.0};
|
||||||
|
for(int c = 0; c < 16; ++c) {
|
||||||
|
const double empiricalLevel = double(maxLevels[c]) * multiplier;
|
||||||
|
XCTAssertLessThanOrEqual(fabs(round(empiricalLevel) - expectedLevels[c]), 2.0, "Fixed attenuation %d was %0.0f; should have been %0.0f", c, empiricalLevel, expectedLevels[c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - ADSR tests
|
// MARK: - ADSR tests
|
||||||
|
|
||||||
- (void)testADSR {
|
- (void)testADSR {
|
||||||
|
Loading…
Reference in New Issue
Block a user