Add DspChannel and implement the Digital Signal Processor

This commit is contained in:
scawful
2024-04-22 15:53:47 -04:00
parent 917cd26a6e
commit 3eb245f985
2 changed files with 665 additions and 57 deletions

View File

@@ -5,6 +5,7 @@
#include <functional>
#include <vector>
#include "app/emu/audio/spc700.h"
#include "app/emu/memory/memory.h"
namespace yaze {
@@ -12,6 +13,41 @@ namespace app {
namespace emu {
namespace audio {
typedef struct DspChannel {
// pitch
uint16_t pitch;
uint16_t pitchCounter;
bool pitchModulation;
// brr decoding
int16_t decodeBuffer[12];
uint8_t bufferOffset;
uint8_t srcn;
uint16_t decodeOffset;
uint8_t blockOffset; // offset within brr block
uint8_t brrHeader;
bool useNoise;
uint8_t startDelay;
// adsr, envelope, gain
uint8_t adsrRates[4]; // attack, decay, sustain, gain
uint8_t adsrState; // 0: attack, 1: decay, 2: sustain, 3: release
uint8_t sustainLevel;
uint8_t gainSustainLevel;
bool useGain;
uint8_t gainMode;
bool directGain;
uint16_t gainValue; // for direct gain
uint16_t preclampGain; // for bent increase
uint16_t gain;
// keyon/off
bool keyOn;
bool keyOff;
// output
int16_t sampleOut; // final sample, to be multiplied by channel volume
int8_t volumeL;
int8_t volumeR;
bool echoEnable;
} DspChannel;
/**
* The S-DSP is a digital signal processor generating the sound data.
*
@@ -31,77 +67,91 @@ namespace audio {
* There are 8 voices, numbered 0 to 7.
* Each voice X has 10 registers in the range $X0-$X9.
*
* | Name | Address | Bits | Notes |
* | Name | Address | Bits | Notes |
* |---------|---------|-----------|--------------------------------------------------------|
* | VOL (L) | $X0 | SVVV VVVV | Left channel volume, signed. |
* | VOL (R) | $X1 | SVVV VVVV | Right channel volume, signed. |
* | P (L) | $X2 | LLLL LLLL | Low 8 bits of sample pitch. |
* | P (H) | $X3 | --HH HHHH | High 6 bits of sample pitch. |
* | SCRN | $X4 | SSSS SSSS | Selects a sample source entry from the directory. |
* | ADSR (1)| $X5 | EDDD AAAA | ADSR enable (E), decay rate (D), attack rate (A). |
* | ADSR (2)| $X6 | SSSR RRRR | Sustain level (S), release rate (R). |
* | GAIN | $X7 | 0VVV VVVV 1MMV VVVV | Mode (M), value (V). |
* | ENVX | $X8 | 0VVV VVVV | Reads current 7-bit value of ADSR/GAIN envelope. |
* | OUTX | $X9 | SVVV VVVV | Reads signed 8-bit value of current sample wave |
* | | | | multiplied by ENVX, before applying VOL. |
* | VOL (L) | $X0 | SVVV VVVV | Left channel volume, signed. | | VOL (R) |
* $X1 | SVVV VVVV | Right channel volume, signed. | | P (L) | $X2 |
* LLLL LLLL | Low 8 bits of sample pitch. | | P (H)
* | $X3 | --HH HHHH | High 6 bits of sample pitch. | | SCRN | $X4 |
* SSSS SSSS | Selects a sample source entry from the directory. | | ADSR
* (1)| $X5 | EDDD AAAA | ADSR enable (E), decay rate (D), attack rate (A).
* | | ADSR (2)| $X6 | SSSR RRRR | Sustain level (S), release rate (R). | |
* GAIN | $X7 | 0VVV VVVV 1MMV VVVV | Mode (M), value (V). | | ENVX |
* $X8 | 0VVV VVVV | Reads current 7-bit value of ADSR/GAIN envelope. | |
* OUTX | $X9 | SVVV VVVV | Reads signed 8-bit value of current sample
* wave | | | | | multiplied by ENVX, before
* applying VOL. |
*/
class Dsp {
public:
Dsp(AudioRam& aram) : aram_(aram) {}
void Reset();
void Cycle();
void HandleEcho();
void CycleChannel(int ch);
void HandleNoise();
void HandleGain(int ch);
bool CheckCounter(int rate);
void DecodeBrr(int ch);
uint8_t Read(uint8_t adr);
void Write(uint8_t adr, uint8_t val);
int16_t GetSample(int ch);
// void GetSamples(int16_t* sampleData, int samplesPerFrame);
void GetSamples(int16_t* sample_data, int samples_per_frame, bool pal_timing);
private:
int16_t sample_buffer_[0x400 * 2]; // (1024 samples, *2 for stereo)
int16_t sample_offset_; // current offset in samplebuffer
static const size_t kNumVoices = 8;
static const size_t kNumVoiceRegs = 10;
static const size_t kNumGlobalRegs = 15;
AudioRam& aram_;
enum class VoiceState { OFF, ATTACK, DECAY, SUSTAIN, RELEASE };
struct Voice {
int8_t vol_left; // x0
int8_t vol_right; // x1
uint8_t pitch_low; // x2
uint8_t pitch_high; // x3
uint8_t source_number; // x4
uint8_t adsr1; // x5
uint8_t adsr2; // x6
uint8_t gain; // x7
uint8_t envx; // x8 (read-only)
int8_t outx; // x9 (read-only)
VoiceState state = VoiceState::OFF;
uint16_t current_amplitude = 0; // Current amplitude value used for ADSR
uint16_t decay_level; // Calculated decay level based on ADSR settings
};
Voice voices_[8];
// Global DSP registers
uint8_t mvol_left; // 0C
uint8_t mvol_right; // 0D
uint8_t evol_left; // 0E
uint8_t evol_right; // 0F
uint8_t kon; // 10
uint8_t koff; // 11
uint8_t flags; // 12
uint8_t endx; // 13 (read-only)
// Global registers
std::vector<uint8_t> globalRegs = std::vector<uint8_t>(kNumGlobalRegs, 0x00);
static const uint16_t ENVELOPE_MAX = 2047; // $7FF
// Attack times in ms
const std::vector<uint32_t> attackTimes = {
4100, 2600, 1500, 1000, 640, 380, 260, 160, 96, 64, 40, 24, 16, 10, 6, 0};
// Decay times in ms
const std::vector<uint32_t> decayTimes = {1200, 740, 440, 290,
180, 110, 74, 37};
// mirror ram
uint8_t ram[0x80];
// 8 channels
DspChannel channel[8];
// overarching
uint16_t counter;
uint16_t dirPage;
bool evenCycle;
bool mute;
bool reset;
int8_t masterVolumeL;
int8_t masterVolumeR;
// accumulation
int16_t sampleOutL;
int16_t sampleOutR;
int16_t echoOutL;
int16_t echoOutR;
// noise
int16_t noiseSample;
uint8_t noiseRate;
// echo
bool echoWrites;
int8_t echoVolumeL;
int8_t echoVolumeR;
int8_t feedbackVolume;
uint16_t echoBufferAdr;
uint16_t echoDelay;
uint16_t echoLength;
uint16_t echoBufferIndex;
uint8_t firBufferIndex;
int8_t firValues[8];
int16_t firBufferL[8];
int16_t firBufferR[8];
// sample ring buffer (1024 samples, *2 for stereo)
int16_t sampleBuffer[0x400 * 2];
uint16_t sampleOffset; // current offset in samplebuffer
};
} // namespace audio