Skip to content

Commit

Permalink
Reduce use of magic numbers in synth module
Browse files Browse the repository at this point in the history
  • Loading branch information
rhargreaves committed Feb 19, 2025
1 parent 4a3c1e5 commit ff0cbd8
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 28 deletions.
53 changes: 29 additions & 24 deletions src/synth.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "synth.h"
#include "bits.h"
#include "debug.h"
#include "ym2612_regs.h"

static Global global = { .lfoEnable = 1, .lfoFrequency = 0, .specialMode = false };
static FmChannel fmChannels[MAX_FM_CHANS];
Expand Down Expand Up @@ -75,13 +76,13 @@ static void updateChannel(u8 chan)

void synth_noteOn(u8 channel)
{
writeRegSafe(0, 0x28, 0xF0 + keyOnOffRegOffset(channel));
writeRegSafe(0, YM_KEY_ON_OFF, 0xF0 + keyOnOffRegOffset(channel));
SET_BIT(noteOn, channel);
}

void synth_noteOff(u8 channel)
{
writeRegSafe(0, 0x28, keyOnOffRegOffset(channel));
writeRegSafe(0, YM_KEY_ON_OFF, keyOnOffRegOffset(channel));
CLEAR_BIT(noteOn, channel);
}

Expand Down Expand Up @@ -298,69 +299,73 @@ static Operator* getOperator(u8 channel, u8 operator)

static void writeGlobalLfo(void)
{
writeRegSafe(0, 0x22, (global.lfoEnable << 3) | global.lfoFrequency);
writeRegSafe(0, YM_LFO_ENABLE, (global.lfoEnable << 3) | global.lfoFrequency);
}

static void writeOctaveAndFrequency(u8 channel)
{
FmChannel* chan = &fmChannels[channel];
writeChannelReg(channel, 0xA4, (chan->freqNumber >> 8) | (chan->octave << 3));
writeChannelReg(channel, 0xA0, chan->freqNumber);
writeChannelReg(channel, YM_BASE_FREQ_MSB_BLK, (chan->freqNumber >> 8) | (chan->octave << 3));
writeChannelReg(channel, YM_BASE_FREQ_LSB, chan->freqNumber);
}

static void writeAlgorithmAndFeedback(u8 channel)
{
FmChannel* chan = &fmChannels[channel];
writeChannelReg(channel, 0xB0, (chan->feedback << 3) + chan->algorithm);
writeChannelReg(channel, YM_BASE_ALGORITHM_FEEDBACK, (chan->feedback << 3) + chan->algorithm);
}

static void writeStereoAmsFms(u8 channel)
{
FmChannel* chan = &fmChannels[channel];
writeChannelReg(channel, 0xB4, (chan->stereo << 6) + (chan->ams << 4) + chan->fms);
writeChannelReg(
channel, YM_BASE_STEREO_AMS_PMS, (chan->stereo << 6) + (chan->ams << 4) + chan->fms);
}

static void writeOperatorMultipleAndDetune(u8 channel, u8 operator)
{
Operator* op = getOperator(channel, operator);
writeOperatorReg(channel, operator, 0x30, op->multiple + (op->detune << 4));
writeOperatorReg(channel, operator, YM_BASE_MULTIPLE_DETUNE, op->multiple + (op->detune << 4));
}

static void writeOperatorRateScalingAndAttackRate(u8 channel, u8 operator)
{
Operator* op = getOperator(channel, operator);
writeOperatorReg(channel, operator, 0x50, op->attackRate + (op->rateScaling << 6));
writeOperatorReg(channel, operator, YM_BASE_ATTACK_RATE_SCALING_RATE,
op->attackRate + (op->rateScaling << 6));
}

static void writeOperatorAmplitudeModulationAndDecayRate(u8 channel, u8 operator)
{
Operator* op = getOperator(channel, operator);
writeOperatorReg(channel, operator, 0x60, op->decayRate + (op->amplitudeModulation << 7));
writeOperatorReg(channel, operator, YM_BASE_DECAY_RATE_AM_ENABLE,
op->decayRate + (op->amplitudeModulation << 7));
}

static void writeOperatorSustainRate(u8 channel, u8 operator)
{
Operator* op = getOperator(channel, operator);
writeOperatorReg(channel, operator, 0x70, op->sustainRate);
writeOperatorReg(channel, operator, YM_BASE_SUSTAIN_RATE, op->sustainRate);
}

static void writeOperatorReleaseRateAndSustainLevel(u8 channel, u8 operator)
{
Operator* op = getOperator(channel, operator);
writeOperatorReg(channel, operator, 0x80, op->releaseRate + (op->sustainLevel << 4));
writeOperatorReg(channel, operator, YM_BASE_RELEASE_RATE_SUSTAIN_LEVEL,
op->releaseRate + (op->sustainLevel << 4));
}

static void writeOperatorSsgEg(u8 channel, u8 operator)
{
Operator* op = getOperator(channel, operator);
writeOperatorReg(channel, operator, 0x90, op->ssgEg);
writeOperatorReg(channel, operator, YM_BASE_SSG_EG, op->ssgEg);
}

static void writeOperatorTotalLevel(u8 channel, u8 operator)
{
Operator* op = getOperator(channel, operator);
writeOperatorReg(
channel, operator, 0x40, effectiveTotalLevel(channel, operator, op->totalLevel));
writeOperatorReg(channel, operator, YM_BASE_TOTAL_LEVEL,
effectiveTotalLevel(channel, operator, op->totalLevel));
}

static u8 effectiveTotalLevel(u8 channel, u8 operator, u8 totalLevel)
Expand Down Expand Up @@ -402,7 +407,7 @@ void synth_setParameterUpdateCallback(ParameterUpdatedCallback* cb)

static void writeSpecialModeReg(void)
{
writeRegSafe(0, 0x27, global.specialMode << 6);
writeRegSafe(0, YM_CH3_MODE, global.specialMode << 6);
}

void synth_setSpecialMode(bool enable)
Expand All @@ -415,14 +420,14 @@ void synth_setSpecialMode(bool enable)
void synth_specialModePitch(u8 op, u8 octave, u16 freqNumber)
{
u8 offset = (op + 1) % 3;
writeRegSafe(0, 0xAC + offset, (freqNumber >> 8) | (octave << 3));
writeRegSafe(0, 0xA8 + offset, freqNumber);
writeRegSafe(0, YM_CH3SM_BASE_FREQ_MSB_BLK + offset, (freqNumber >> 8) | (octave << 3));
writeRegSafe(0, YM_CH3SM_BASE_FREQ_LSB + offset, freqNumber);
}

void synth_specialModeVolume(u8 operator, u8 volume)
{
Operator* op = getOperator(CH_SPECIAL_MODE, operator);
if (!isOutputOperator(fmChannels[CH_SPECIAL_MODE].algorithm, operator)) {
Operator* op = getOperator(CH3_SPECIAL_MODE, operator);
if (!isOutputOperator(fmChannels[CH3_SPECIAL_MODE].algorithm, operator)) {
return;
}

Expand All @@ -431,8 +436,8 @@ void synth_specialModeVolume(u8 operator, u8 volume)
u8 inverseNewTotalLevel = (u16)inverseTotalLevel * (u16)logarithmicVolume / (u16)0x7F;
u8 newTotalLevel = 0x7F - inverseNewTotalLevel;

writeOperatorReg(CH_SPECIAL_MODE, operator, 0x40,
effectiveTotalLevel(CH_SPECIAL_MODE, operator, newTotalLevel));
writeOperatorReg(CH3_SPECIAL_MODE, operator, YM_BASE_TOTAL_LEVEL,
effectiveTotalLevel(CH3_SPECIAL_MODE, operator, newTotalLevel));
}

void synth_directWriteYm2612(u8 part, u8 reg, u8 data)
Expand All @@ -442,7 +447,7 @@ void synth_directWriteYm2612(u8 part, u8 reg, u8 data)

void synth_enableDac(bool enable)
{
writeRegSafe(0, 0x2B, enable ? 0x80 : 0);
writeRegSafe(0, YM_DAC_ENABLE, enable ? 0x80 : 0);
}

static void writeRegSafe(u8 part, u8 reg, u8 data)
Expand All @@ -458,6 +463,6 @@ static void writeRegSafe(u8 part, u8 reg, u8 data)

static void releaseZ80Bus(void)
{
YM2612_write(0, 0x2A); // Latch reg address for PCM driver
YM2612_write(0, YM_DAC_DATA); // Latch reg address for PCM driver
Z80_releaseBus();
}
2 changes: 1 addition & 1 deletion src/synth.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#define MAX_FM_CHANS 6
#define FM_ALGORITHMS 8

#define CH_SPECIAL_MODE 2
#define CH3_SPECIAL_MODE 2

#define STEREO_MODE_CENTRE 3
#define STEREO_MODE_RIGHT 1
Expand Down
51 changes: 51 additions & 0 deletions src/ym2612_regs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

// Global
#define YM_LFO_ENABLE 0x22
#define YM_CH3_MODE 0x27
#define YM_KEY_ON_OFF 0x28
#define YM_DAC_DATA 0x2A
#define YM_DAC_ENABLE 0x2B
#define YM_TEST 0x2C

// Channel 1, Operator 1
#define YM_BASE_MULTIPLE_DETUNE 0x30
#define YM_BASE_TOTAL_LEVEL 0x40
#define YM_BASE_ATTACK_RATE_SCALING_RATE 0x50
#define YM_BASE_DECAY_RATE_AM_ENABLE 0x60
#define YM_BASE_SUSTAIN_RATE 0x70
#define YM_BASE_RELEASE_RATE_SUSTAIN_LEVEL 0x80
#define YM_BASE_SSG_EG 0x90

// Channel 1
#define YM_BASE_FREQ_LSB 0xA0
#define YM_BASE_FREQ_MSB_BLK 0xA4
#define YM_BASE_ALGORITHM_FEEDBACK 0xB0
#define YM_BASE_STEREO_AMS_PMS 0xB4

// Channel 3 Special Mode
#define YM_CH3SM_BASE_FREQ_LSB 0xA8
#define YM_CH3SM_BASE_FREQ_MSB_BLK 0xAC

// Operator Offsets
#define YM_OP2_OFFSET 0x08
#define YM_OP3_OFFSET 0x04
#define YM_OP4_OFFSET 0x0C

// Test Helpers
#define YM_CH3SM_OP_SELECT(baseReg, op) (baseReg + ((op + 1) % 3))
#define YM_CH_SELECT(baseReg, channel) (baseReg + (channel % 3))
#define YM_OP_REG_INDEX(op) ((op) == 1 ? 2 : ((op) == 2 ? 1 : (op)))
#define YM_OP_SELECT(baseReg, op) (baseReg + (YM_OP_REG_INDEX(op) * 4))

#define YM_CH1 0
#define YM_CH2 1
#define YM_CH3 2
#define YM_CH4 0
#define YM_CH5 1
#define YM_CH6 2

#define YM_OP1 0
#define YM_OP2 1
#define YM_OP3 2
#define YM_OP4 3
4 changes: 2 additions & 2 deletions tests/system/test_e2e.c
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ void test_sets_separate_ch3_operator_frequencies(void** state)
stub_usb_receive_note_on(TEST_MIDI_CHANNEL_11 + op, 60, 120);
expect_ym2612_write_reg(0, upperRegs[op], 0x22);
expect_ym2612_write_reg(0, lowerRegs[op], 0x84);
expect_ym2612_write_operator(CH_SPECIAL_MODE, op, 0x40, tlValues[op]);
expect_ym2612_write_operator(CH3_SPECIAL_MODE, op, 0x40, tlValues[op]);
midi_receiver_read();
}
}
Expand All @@ -259,7 +259,7 @@ void test_pitch_bends_ch3_special_mode_operators(void** state)
stub_usb_receive_note_on(TEST_MIDI_CHANNEL_11, 60, 120);
expect_ym2612_write_reg(0, 0xAD, 0x22);
expect_ym2612_write_reg(0, 0xA9, 0x84);
expect_ym2612_write_operator_any_data(CH_SPECIAL_MODE, 0, 0x40);
expect_ym2612_write_operator_any_data(CH3_SPECIAL_MODE, 0, 0x40);
midi_receiver_read();

stub_usb_receive_pitch_bend(TEST_MIDI_CHANNEL_11, 0x4000);
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_synth.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ void test_synth_sets_ch3_special_mode_op_tl_only_if_output_operator(UNUSED void*
int algorithm = (*(int*)(*state));

const u8 baseReg = 0x40;
const u8 chan = CH_SPECIAL_MODE;
const u8 chan = CH3_SPECIAL_MODE;

expect_ym2612_write_channel(chan, 0xB0, algorithm);
__real_synth_algorithm(chan, algorithm);
Expand Down

0 comments on commit ff0cbd8

Please sign in to comment.