From 99fa86a67ecd4358790c838742a8fa93522ca3ef Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 20 Apr 2020 18:40:47 -0400 Subject: [PATCH] Adds a test for lookup sine. And fixes lookup sine. --- Components/OPL2/Implementation/Tables.hpp | 2 +- OSBindings/Mac/Clock SignalTests/OPLTests.mm | 60 ++++++++++++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/Components/OPL2/Implementation/Tables.hpp b/Components/OPL2/Implementation/Tables.hpp index 66fcf71fd..c7839785d 100644 --- a/Components/OPL2/Implementation/Tables.hpp +++ b/Components/OPL2/Implementation/Tables.hpp @@ -82,7 +82,7 @@ constexpr LogSign negative_log_sin(int x) { constexpr int16_t mask[] = { 0, 255 }; return { - .log = log_sin[x & 255] ^ mask[(x >> 8) & 1], + .log = log_sin[(x & 255) ^ mask[(x >> 8) & 1]], .sign = sign[(x >> 9) & 1] }; } diff --git a/OSBindings/Mac/Clock SignalTests/OPLTests.mm b/OSBindings/Mac/Clock SignalTests/OPLTests.mm index 1c39fe0b4..e4e8faeff 100644 --- a/OSBindings/Mac/Clock SignalTests/OPLTests.mm +++ b/OSBindings/Mac/Clock SignalTests/OPLTests.mm @@ -9,6 +9,7 @@ #import #include "OPL2.hpp" +#include @interface OPLTests: XCTestCase @end @@ -16,6 +17,18 @@ @implementation OPLTests { } +- (void)testSineLookup { + for(int c = 0; c < 1024; ++c) { + const auto logSin = Yamaha::OPL::negative_log_sin(c); + const auto level = Yamaha::OPL::power_two(logSin); + + double fl_angle = double(c) * M_PI / 512.0; + double fl_level = sin(fl_angle); + + XCTAssertLessThanOrEqual(fabs(fl_level - double(level) / 4096.0), 0.01, "Sine varies by more than 0.01 at angle %d", c); + } +} + - (void)testADSR { Yamaha::OPL::Operator test_operator; Yamaha::OPL::OperatorState test_state; @@ -23,18 +36,55 @@ test_operator.set_attack_decay(0x88); test_operator.set_sustain_release(0x88); - // While key is off, ADSR attenuation should remain above 511. + // While key is off, output level should remain at 0. for(int c = 0; c < 1024; ++c) { - test_operator.update(test_state, false, 0, 0); - XCTAssertGreaterThanOrEqual(test_state.attenuation, 511); + test_operator.update(test_state, false, 0, 0, 0); + XCTAssertGreaterThanOrEqual(test_state.level(), 0); } // Set key on... for(int c = 0; c < 4096; ++c) { - test_operator.update(test_state, true, 0, 0); - NSLog(@"%d", test_state.attenuation); + test_operator.update(test_state, true, 0, 0, 0); + NSLog(@"%d", test_state.level()); } +} +- (void)testFM { + Yamaha::OPL::Operator modulator, carrier; + Yamaha::OPL::Channel channel; + + // Set: AM = 0, PM = 0, EG = 1, KR = 0, MUL = 0 + modulator.set_am_vibrato_hold_sustain_ksr_multiple(0x20); + carrier.set_am_vibrato_hold_sustain_ksr_multiple(0x20); + + // Set: KL = 0, TL = 0 + modulator.set_scaling_output(0x3f); + carrier.set_scaling_output(0); + + // Set: waveform = 0. + modulator.set_waveform(0); + carrier.set_waveform(0); + + // Set FB = 0, use FM synthesis. + channel.set_feedback_mode(1); + + // Set: AR = 15, DR = 0. + modulator.set_attack_decay(0xf0); + carrier.set_attack_decay(0xf0); + + // Set: SL = 0, RR = 15. + modulator.set_sustain_release(0x0f); + carrier.set_sustain_release(0x0f); + + // Set 16384 samples for 1 period, and key-on. + channel.set_frequency_low(0x40); + channel.set_9bit_frequency_octave_key_on(0x10); + + // Grab a bunch of samples. + for(int c = 0; c < 16384; ++c) { + printf("%d\n", channel.update(&modulator, &carrier)); + } + printf("\n"); } @end