Add DspChannel and implement the Digital Signal Processor
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user