Files
yaze/src/app/emu/audio/dsp.h

160 lines
4.4 KiB
C++

#ifndef YAZE_APP_EMU_AUDIO_S_DSP_H
#define YAZE_APP_EMU_AUDIO_S_DSP_H
#include <cstdint>
#include <functional>
#include <vector>
#include "app/emu/audio/spc700.h"
#include "app/emu/memory/memory.h"
namespace yaze {
namespace emu {
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.
*
* A DSP register can be selected with $F2, after which it can be read or
* written at $F3. Often it is useful to load the register address into A, and
* the value to send in Y, so that MOV $F2, YA can be used to do both in one
* 16-bit instruction.
*
* The DSP register address space only has 7 bits. The high bit of $F2, if set,
* will make the selected register read-only via $F3.
*
* When initializing the DSP registers for the first time, take care not to
* accidentally enable echo writeback via FLG, because it will immediately begin
* overwriting values in RAM.
*
* Voices
* There are 8 voices, numbered 0 to 7.
* Each voice X has 10 registers in the range $X0-$X9.
*
* | 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. |
*/
class Dsp {
public:
Dsp(std::vector<uint8_t>& aram) : aram_(aram) {}
void NewFrame();
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* 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
std::vector<uint8_t>& aram_;
// 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
uint32_t lastFrameBoundary;
};
} // namespace emu
} // namespace yaze
#endif // YAZE_APP_EMU_AUDIO_S_DSP_H